You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.js 153 kB

Support unicode emojis and remove emojify.js (#11032) * Support unicode emojis and remove emojify.js This PR replaces all use of emojify.js and adds unicode emoji support to various areas of gitea. This works in a few ways: First it adds emoji parsing support into gitea itself. This allows us to * Render emojis from valid alias (:smile:) * Detect unicode emojis and let us put them in their own class with proper aria-labels and styling * Easily allow for custom "emoji" * Support all emoji rendering and features without javascript * Uses plain unicode and lets the system render in appropriate emoji font * Doesn't leave us relying on external sources for updates/fixes/features That same list of emoji is also used to create a json file which replaces the part of emojify.js that populates the emoji search tribute. This file is about 35KB with GZIP turned on and I've set it to load after the page renders to not hinder page load time (and this removes loading emojify.js also) For custom "emoji" it uses a pretty simple scheme of just looking for /emojis/img/name.png where name is something a user has put in the "allowed reactions" setting we already have. The gitea reaction that was previously hard coded into a forked copy of emojify.js is included and works as a custom reaction under this method. The emoji data sourced here is from https://github.com/github/gemoji which is the gem library Github uses for their emoji rendering (and a data source for other sites). So we should be able to easily render any emoji and :alias: that Github can, removing any errors from migrated content. They also update it as well, so we can sync when there are new unicode emoji lists released. I've included a slimmed down and slightly modified forked copy of https://github.com/knq/emoji to make up our own emoji module. The code is pretty straight forward and again allows us to have a lot of flexibility in what happens. I had seen a few comments about performance in some of the other threads if we render this ourselves, but there doesn't seem to be any issue here. In a test it can parse, convert, and render 1,000 emojis inside of a large markdown table in about 100ms on my laptop (which is many more emojis than will ever be in any normal issue). This also prevents any flickering and other weirdness from using javascript to render some things while using go for others. Not included here are image fall back URLS. I don't really think they are necessary for anything new being written in 2020. However, managing the emoji ourselves would allow us to add these as a feature later on if it seems necessary. Fixes: https://github.com/go-gitea/gitea/issues/9182 Fixes: https://github.com/go-gitea/gitea/issues/8974 Fixes: https://github.com/go-gitea/gitea/issues/8953 Fixes: https://github.com/go-gitea/gitea/issues/6628 Fixes: https://github.com/go-gitea/gitea/issues/5130 * add new shared function emojiHTML * don't increase emoji size in issue title * Update templates/repo/issue/view_content/add_reaction.tmpl Co-Authored-By: 6543 <6543@obermui.de> * Support for emoji rendering in various templates * Render code and review comments as they should be * Better way to handle mail subjects * insert unicode from tribute selection * Add template helper for plain text when needed * Use existing replace function I forgot about * Don't include emoji greater than Unicode Version 12 Only include emoji and aliases in JSON * Update build/generate-emoji.go * Tweak regex slightly to really match everything including random invisible characters. Run tests for every emoji we have * final updates * code review * code review * hard code gitea custom emoji to match previous behavior * Update .eslintrc Co-Authored-By: silverwind <me@silverwind.io> * disable preempt Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com>
5 years ago
3 years ago
2 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
2 years ago
3 years ago
2 years ago
3 years ago
3 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
3 years ago
2 years ago
2 years ago
3 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
Add Organization Wide Labels (#10814) * Add organization wide labels Implement organization wide labels similar to organization wide webhooks. This lets you create individual labels for organizations that can be used for all repos under that organization (so being able to reuse the same label across multiple repos). This makes it possible for small organizations with many repos to use labels effectively. Fixes #7406 * Add migration * remove comments * fix tests * Update options/locale/locale_en-US.ini Removed unused translation string * show org labels in issue search label filter * Use more clear var name * rename migration after merge from master * comment typo * update migration again after rebase with master * check for orgID <=0 per guillep2k review * fmt * Apply suggestions from code review Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * remove unused code * Make sure RepoID is 0 when searching orgID per code review * more changes/code review requests * More descriptive translation var per code review * func description/delete comment when issue label deleted instead of hiding it * remove comment * only use issues in that repo when calculating number of open issues for org label on repo label page * Add integration test for IssuesSearch API with labels * remove unused function * Update models/issue_label.go Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * Use subquery in GetLabelIDsInReposByNames * Fix tests to use correct orgID * fix more tests * IssuesSearch api now uses new BuildLabelNamesIssueIDsCondition. Add a few more tests as well * update comment for clarity * Revert previous code change now that we can use the new BuildLabelNamesIssueIDsCondition * Don't sort repos by date in IssuesSearch API After much debugging I've found a strange issue where in some cases MySQL will return a different result than other enigines if a query is sorted by a null collumn. For example with our integration test data where we don't set updated_unix in repository fixtures: SELECT `id`, `owner_id`, `owner_name`, `lower_name`, `name`, `description`, `website`, `original_service_type`, `original_url`, `default_branch`, `num_watches`, `num_stars`, `num_forks`, `num_issues`, `num_closed_issues`, `num_pulls`, `num_closed_pulls`, `num_milestones`, `num_closed_milestones`, `is_private`, `is_empty`, `is_archived`, `is_mirror`, `status`, `is_fork`, `fork_id`, `is_template`, `template_id`, `size`, `is_fsck_enabled`, `close_issues_via_commit_in_any_branch`, `topics`, `avatar`, `created_unix`, `updated_unix` FROM `repository` ORDER BY updated_unix DESC LIMIT 15 OFFSET 45 Returns different results for MySQL than other engines. However, the similar query: SELECT `id`, `owner_id`, `owner_name`, `lower_name`, `name`, `description`, `website`, `original_service_type`, `original_url`, `default_branch`, `num_watches`, `num_stars`, `num_forks`, `num_issues`, `num_closed_issues`, `num_pulls`, `num_closed_pulls`, `num_milestones`, `num_closed_milestones`, `is_private`, `is_empty`, `is_archived`, `is_mirror`, `status`, `is_fork`, `fork_id`, `is_template`, `template_id`, `size`, `is_fsck_enabled`, `close_issues_via_commit_in_any_branch`, `topics`, `avatar`, `created_unix`, `updated_unix` FROM `repository` ORDER BY updated_unix DESC LIMIT 15 OFFSET 30 Returns the same results. This causes integration tests to fail on MySQL in certain cases but would never show up in a real installation. Since this API call always returns issues based on the optionally provided repo_priority_id or the issueID itself, there is no change to results by changing the repo sorting method used to get ids earlier in the function. * linter is back! * code review * remove now unused option * Fix newline at end of files * more unused code * update to master * check for matching ids before query * Update models/issue_label.go Co-Authored-By: 6543 <6543@obermui.de> * Update models/issue_label.go * update comments * Update routers/org/setting.go Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com> Co-authored-by: 6543 <6543@obermui.de>
5 years ago
4 years ago
3 years ago
Add Organization Wide Labels (#10814) * Add organization wide labels Implement organization wide labels similar to organization wide webhooks. This lets you create individual labels for organizations that can be used for all repos under that organization (so being able to reuse the same label across multiple repos). This makes it possible for small organizations with many repos to use labels effectively. Fixes #7406 * Add migration * remove comments * fix tests * Update options/locale/locale_en-US.ini Removed unused translation string * show org labels in issue search label filter * Use more clear var name * rename migration after merge from master * comment typo * update migration again after rebase with master * check for orgID <=0 per guillep2k review * fmt * Apply suggestions from code review Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * remove unused code * Make sure RepoID is 0 when searching orgID per code review * more changes/code review requests * More descriptive translation var per code review * func description/delete comment when issue label deleted instead of hiding it * remove comment * only use issues in that repo when calculating number of open issues for org label on repo label page * Add integration test for IssuesSearch API with labels * remove unused function * Update models/issue_label.go Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * Use subquery in GetLabelIDsInReposByNames * Fix tests to use correct orgID * fix more tests * IssuesSearch api now uses new BuildLabelNamesIssueIDsCondition. Add a few more tests as well * update comment for clarity * Revert previous code change now that we can use the new BuildLabelNamesIssueIDsCondition * Don't sort repos by date in IssuesSearch API After much debugging I've found a strange issue where in some cases MySQL will return a different result than other enigines if a query is sorted by a null collumn. For example with our integration test data where we don't set updated_unix in repository fixtures: SELECT `id`, `owner_id`, `owner_name`, `lower_name`, `name`, `description`, `website`, `original_service_type`, `original_url`, `default_branch`, `num_watches`, `num_stars`, `num_forks`, `num_issues`, `num_closed_issues`, `num_pulls`, `num_closed_pulls`, `num_milestones`, `num_closed_milestones`, `is_private`, `is_empty`, `is_archived`, `is_mirror`, `status`, `is_fork`, `fork_id`, `is_template`, `template_id`, `size`, `is_fsck_enabled`, `close_issues_via_commit_in_any_branch`, `topics`, `avatar`, `created_unix`, `updated_unix` FROM `repository` ORDER BY updated_unix DESC LIMIT 15 OFFSET 45 Returns different results for MySQL than other engines. However, the similar query: SELECT `id`, `owner_id`, `owner_name`, `lower_name`, `name`, `description`, `website`, `original_service_type`, `original_url`, `default_branch`, `num_watches`, `num_stars`, `num_forks`, `num_issues`, `num_closed_issues`, `num_pulls`, `num_closed_pulls`, `num_milestones`, `num_closed_milestones`, `is_private`, `is_empty`, `is_archived`, `is_mirror`, `status`, `is_fork`, `fork_id`, `is_template`, `template_id`, `size`, `is_fsck_enabled`, `close_issues_via_commit_in_any_branch`, `topics`, `avatar`, `created_unix`, `updated_unix` FROM `repository` ORDER BY updated_unix DESC LIMIT 15 OFFSET 30 Returns the same results. This causes integration tests to fail on MySQL in certain cases but would never show up in a real installation. Since this API call always returns issues based on the optionally provided repo_priority_id or the issueID itself, there is no change to results by changing the repo sorting method used to get ids earlier in the function. * linter is back! * code review * remove now unused option * Fix newline at end of files * more unused code * update to master * check for matching ids before query * Update models/issue_label.go Co-Authored-By: 6543 <6543@obermui.de> * Update models/issue_label.go * update comments * Update routers/org/setting.go Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com> Co-authored-by: 6543 <6543@obermui.de>
5 years ago
3 years ago
Add Organization Wide Labels (#10814) * Add organization wide labels Implement organization wide labels similar to organization wide webhooks. This lets you create individual labels for organizations that can be used for all repos under that organization (so being able to reuse the same label across multiple repos). This makes it possible for small organizations with many repos to use labels effectively. Fixes #7406 * Add migration * remove comments * fix tests * Update options/locale/locale_en-US.ini Removed unused translation string * show org labels in issue search label filter * Use more clear var name * rename migration after merge from master * comment typo * update migration again after rebase with master * check for orgID <=0 per guillep2k review * fmt * Apply suggestions from code review Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * remove unused code * Make sure RepoID is 0 when searching orgID per code review * more changes/code review requests * More descriptive translation var per code review * func description/delete comment when issue label deleted instead of hiding it * remove comment * only use issues in that repo when calculating number of open issues for org label on repo label page * Add integration test for IssuesSearch API with labels * remove unused function * Update models/issue_label.go Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * Use subquery in GetLabelIDsInReposByNames * Fix tests to use correct orgID * fix more tests * IssuesSearch api now uses new BuildLabelNamesIssueIDsCondition. Add a few more tests as well * update comment for clarity * Revert previous code change now that we can use the new BuildLabelNamesIssueIDsCondition * Don't sort repos by date in IssuesSearch API After much debugging I've found a strange issue where in some cases MySQL will return a different result than other enigines if a query is sorted by a null collumn. For example with our integration test data where we don't set updated_unix in repository fixtures: SELECT `id`, `owner_id`, `owner_name`, `lower_name`, `name`, `description`, `website`, `original_service_type`, `original_url`, `default_branch`, `num_watches`, `num_stars`, `num_forks`, `num_issues`, `num_closed_issues`, `num_pulls`, `num_closed_pulls`, `num_milestones`, `num_closed_milestones`, `is_private`, `is_empty`, `is_archived`, `is_mirror`, `status`, `is_fork`, `fork_id`, `is_template`, `template_id`, `size`, `is_fsck_enabled`, `close_issues_via_commit_in_any_branch`, `topics`, `avatar`, `created_unix`, `updated_unix` FROM `repository` ORDER BY updated_unix DESC LIMIT 15 OFFSET 45 Returns different results for MySQL than other engines. However, the similar query: SELECT `id`, `owner_id`, `owner_name`, `lower_name`, `name`, `description`, `website`, `original_service_type`, `original_url`, `default_branch`, `num_watches`, `num_stars`, `num_forks`, `num_issues`, `num_closed_issues`, `num_pulls`, `num_closed_pulls`, `num_milestones`, `num_closed_milestones`, `is_private`, `is_empty`, `is_archived`, `is_mirror`, `status`, `is_fork`, `fork_id`, `is_template`, `template_id`, `size`, `is_fsck_enabled`, `close_issues_via_commit_in_any_branch`, `topics`, `avatar`, `created_unix`, `updated_unix` FROM `repository` ORDER BY updated_unix DESC LIMIT 15 OFFSET 30 Returns the same results. This causes integration tests to fail on MySQL in certain cases but would never show up in a real installation. Since this API call always returns issues based on the optionally provided repo_priority_id or the issueID itself, there is no change to results by changing the repo sorting method used to get ids earlier in the function. * linter is back! * code review * remove now unused option * Fix newline at end of files * more unused code * update to master * check for matching ids before query * Update models/issue_label.go Co-Authored-By: 6543 <6543@obermui.de> * Update models/issue_label.go * update comments * Update routers/org/setting.go Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com> Co-authored-by: 6543 <6543@obermui.de>
5 years ago
3 years ago
Add Organization Wide Labels (#10814) * Add organization wide labels Implement organization wide labels similar to organization wide webhooks. This lets you create individual labels for organizations that can be used for all repos under that organization (so being able to reuse the same label across multiple repos). This makes it possible for small organizations with many repos to use labels effectively. Fixes #7406 * Add migration * remove comments * fix tests * Update options/locale/locale_en-US.ini Removed unused translation string * show org labels in issue search label filter * Use more clear var name * rename migration after merge from master * comment typo * update migration again after rebase with master * check for orgID <=0 per guillep2k review * fmt * Apply suggestions from code review Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * remove unused code * Make sure RepoID is 0 when searching orgID per code review * more changes/code review requests * More descriptive translation var per code review * func description/delete comment when issue label deleted instead of hiding it * remove comment * only use issues in that repo when calculating number of open issues for org label on repo label page * Add integration test for IssuesSearch API with labels * remove unused function * Update models/issue_label.go Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * Use subquery in GetLabelIDsInReposByNames * Fix tests to use correct orgID * fix more tests * IssuesSearch api now uses new BuildLabelNamesIssueIDsCondition. Add a few more tests as well * update comment for clarity * Revert previous code change now that we can use the new BuildLabelNamesIssueIDsCondition * Don't sort repos by date in IssuesSearch API After much debugging I've found a strange issue where in some cases MySQL will return a different result than other enigines if a query is sorted by a null collumn. For example with our integration test data where we don't set updated_unix in repository fixtures: SELECT `id`, `owner_id`, `owner_name`, `lower_name`, `name`, `description`, `website`, `original_service_type`, `original_url`, `default_branch`, `num_watches`, `num_stars`, `num_forks`, `num_issues`, `num_closed_issues`, `num_pulls`, `num_closed_pulls`, `num_milestones`, `num_closed_milestones`, `is_private`, `is_empty`, `is_archived`, `is_mirror`, `status`, `is_fork`, `fork_id`, `is_template`, `template_id`, `size`, `is_fsck_enabled`, `close_issues_via_commit_in_any_branch`, `topics`, `avatar`, `created_unix`, `updated_unix` FROM `repository` ORDER BY updated_unix DESC LIMIT 15 OFFSET 45 Returns different results for MySQL than other engines. However, the similar query: SELECT `id`, `owner_id`, `owner_name`, `lower_name`, `name`, `description`, `website`, `original_service_type`, `original_url`, `default_branch`, `num_watches`, `num_stars`, `num_forks`, `num_issues`, `num_closed_issues`, `num_pulls`, `num_closed_pulls`, `num_milestones`, `num_closed_milestones`, `is_private`, `is_empty`, `is_archived`, `is_mirror`, `status`, `is_fork`, `fork_id`, `is_template`, `template_id`, `size`, `is_fsck_enabled`, `close_issues_via_commit_in_any_branch`, `topics`, `avatar`, `created_unix`, `updated_unix` FROM `repository` ORDER BY updated_unix DESC LIMIT 15 OFFSET 30 Returns the same results. This causes integration tests to fail on MySQL in certain cases but would never show up in a real installation. Since this API call always returns issues based on the optionally provided repo_priority_id or the issueID itself, there is no change to results by changing the repo sorting method used to get ids earlier in the function. * linter is back! * code review * remove now unused option * Fix newline at end of files * more unused code * update to master * check for matching ids before query * Update models/issue_label.go Co-Authored-By: 6543 <6543@obermui.de> * Update models/issue_label.go * update comments * Update routers/org/setting.go Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com> Co-authored-by: 6543 <6543@obermui.de>
5 years ago
3 years ago
Add Organization Wide Labels (#10814) * Add organization wide labels Implement organization wide labels similar to organization wide webhooks. This lets you create individual labels for organizations that can be used for all repos under that organization (so being able to reuse the same label across multiple repos). This makes it possible for small organizations with many repos to use labels effectively. Fixes #7406 * Add migration * remove comments * fix tests * Update options/locale/locale_en-US.ini Removed unused translation string * show org labels in issue search label filter * Use more clear var name * rename migration after merge from master * comment typo * update migration again after rebase with master * check for orgID <=0 per guillep2k review * fmt * Apply suggestions from code review Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * remove unused code * Make sure RepoID is 0 when searching orgID per code review * more changes/code review requests * More descriptive translation var per code review * func description/delete comment when issue label deleted instead of hiding it * remove comment * only use issues in that repo when calculating number of open issues for org label on repo label page * Add integration test for IssuesSearch API with labels * remove unused function * Update models/issue_label.go Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * Use subquery in GetLabelIDsInReposByNames * Fix tests to use correct orgID * fix more tests * IssuesSearch api now uses new BuildLabelNamesIssueIDsCondition. Add a few more tests as well * update comment for clarity * Revert previous code change now that we can use the new BuildLabelNamesIssueIDsCondition * Don't sort repos by date in IssuesSearch API After much debugging I've found a strange issue where in some cases MySQL will return a different result than other enigines if a query is sorted by a null collumn. For example with our integration test data where we don't set updated_unix in repository fixtures: SELECT `id`, `owner_id`, `owner_name`, `lower_name`, `name`, `description`, `website`, `original_service_type`, `original_url`, `default_branch`, `num_watches`, `num_stars`, `num_forks`, `num_issues`, `num_closed_issues`, `num_pulls`, `num_closed_pulls`, `num_milestones`, `num_closed_milestones`, `is_private`, `is_empty`, `is_archived`, `is_mirror`, `status`, `is_fork`, `fork_id`, `is_template`, `template_id`, `size`, `is_fsck_enabled`, `close_issues_via_commit_in_any_branch`, `topics`, `avatar`, `created_unix`, `updated_unix` FROM `repository` ORDER BY updated_unix DESC LIMIT 15 OFFSET 45 Returns different results for MySQL than other engines. However, the similar query: SELECT `id`, `owner_id`, `owner_name`, `lower_name`, `name`, `description`, `website`, `original_service_type`, `original_url`, `default_branch`, `num_watches`, `num_stars`, `num_forks`, `num_issues`, `num_closed_issues`, `num_pulls`, `num_closed_pulls`, `num_milestones`, `num_closed_milestones`, `is_private`, `is_empty`, `is_archived`, `is_mirror`, `status`, `is_fork`, `fork_id`, `is_template`, `template_id`, `size`, `is_fsck_enabled`, `close_issues_via_commit_in_any_branch`, `topics`, `avatar`, `created_unix`, `updated_unix` FROM `repository` ORDER BY updated_unix DESC LIMIT 15 OFFSET 30 Returns the same results. This causes integration tests to fail on MySQL in certain cases but would never show up in a real installation. Since this API call always returns issues based on the optionally provided repo_priority_id or the issueID itself, there is no change to results by changing the repo sorting method used to get ids earlier in the function. * linter is back! * code review * remove now unused option * Fix newline at end of files * more unused code * update to master * check for matching ids before query * Update models/issue_label.go Co-Authored-By: 6543 <6543@obermui.de> * Update models/issue_label.go * update comments * Update routers/org/setting.go Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com> Co-authored-by: 6543 <6543@obermui.de>
5 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
Add Organization Wide Labels (#10814) * Add organization wide labels Implement organization wide labels similar to organization wide webhooks. This lets you create individual labels for organizations that can be used for all repos under that organization (so being able to reuse the same label across multiple repos). This makes it possible for small organizations with many repos to use labels effectively. Fixes #7406 * Add migration * remove comments * fix tests * Update options/locale/locale_en-US.ini Removed unused translation string * show org labels in issue search label filter * Use more clear var name * rename migration after merge from master * comment typo * update migration again after rebase with master * check for orgID <=0 per guillep2k review * fmt * Apply suggestions from code review Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * remove unused code * Make sure RepoID is 0 when searching orgID per code review * more changes/code review requests * More descriptive translation var per code review * func description/delete comment when issue label deleted instead of hiding it * remove comment * only use issues in that repo when calculating number of open issues for org label on repo label page * Add integration test for IssuesSearch API with labels * remove unused function * Update models/issue_label.go Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * Use subquery in GetLabelIDsInReposByNames * Fix tests to use correct orgID * fix more tests * IssuesSearch api now uses new BuildLabelNamesIssueIDsCondition. Add a few more tests as well * update comment for clarity * Revert previous code change now that we can use the new BuildLabelNamesIssueIDsCondition * Don't sort repos by date in IssuesSearch API After much debugging I've found a strange issue where in some cases MySQL will return a different result than other enigines if a query is sorted by a null collumn. For example with our integration test data where we don't set updated_unix in repository fixtures: SELECT `id`, `owner_id`, `owner_name`, `lower_name`, `name`, `description`, `website`, `original_service_type`, `original_url`, `default_branch`, `num_watches`, `num_stars`, `num_forks`, `num_issues`, `num_closed_issues`, `num_pulls`, `num_closed_pulls`, `num_milestones`, `num_closed_milestones`, `is_private`, `is_empty`, `is_archived`, `is_mirror`, `status`, `is_fork`, `fork_id`, `is_template`, `template_id`, `size`, `is_fsck_enabled`, `close_issues_via_commit_in_any_branch`, `topics`, `avatar`, `created_unix`, `updated_unix` FROM `repository` ORDER BY updated_unix DESC LIMIT 15 OFFSET 45 Returns different results for MySQL than other engines. However, the similar query: SELECT `id`, `owner_id`, `owner_name`, `lower_name`, `name`, `description`, `website`, `original_service_type`, `original_url`, `default_branch`, `num_watches`, `num_stars`, `num_forks`, `num_issues`, `num_closed_issues`, `num_pulls`, `num_closed_pulls`, `num_milestones`, `num_closed_milestones`, `is_private`, `is_empty`, `is_archived`, `is_mirror`, `status`, `is_fork`, `fork_id`, `is_template`, `template_id`, `size`, `is_fsck_enabled`, `close_issues_via_commit_in_any_branch`, `topics`, `avatar`, `created_unix`, `updated_unix` FROM `repository` ORDER BY updated_unix DESC LIMIT 15 OFFSET 30 Returns the same results. This causes integration tests to fail on MySQL in certain cases but would never show up in a real installation. Since this API call always returns issues based on the optionally provided repo_priority_id or the issueID itself, there is no change to results by changing the repo sorting method used to get ids earlier in the function. * linter is back! * code review * remove now unused option * Fix newline at end of files * more unused code * update to master * check for matching ids before query * Update models/issue_label.go Co-Authored-By: 6543 <6543@obermui.de> * Update models/issue_label.go * update comments * Update routers/org/setting.go Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com> Co-authored-by: 6543 <6543@obermui.de>
5 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
5 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
Add Organization Wide Labels (#10814) * Add organization wide labels Implement organization wide labels similar to organization wide webhooks. This lets you create individual labels for organizations that can be used for all repos under that organization (so being able to reuse the same label across multiple repos). This makes it possible for small organizations with many repos to use labels effectively. Fixes #7406 * Add migration * remove comments * fix tests * Update options/locale/locale_en-US.ini Removed unused translation string * show org labels in issue search label filter * Use more clear var name * rename migration after merge from master * comment typo * update migration again after rebase with master * check for orgID <=0 per guillep2k review * fmt * Apply suggestions from code review Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * remove unused code * Make sure RepoID is 0 when searching orgID per code review * more changes/code review requests * More descriptive translation var per code review * func description/delete comment when issue label deleted instead of hiding it * remove comment * only use issues in that repo when calculating number of open issues for org label on repo label page * Add integration test for IssuesSearch API with labels * remove unused function * Update models/issue_label.go Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * Use subquery in GetLabelIDsInReposByNames * Fix tests to use correct orgID * fix more tests * IssuesSearch api now uses new BuildLabelNamesIssueIDsCondition. Add a few more tests as well * update comment for clarity * Revert previous code change now that we can use the new BuildLabelNamesIssueIDsCondition * Don't sort repos by date in IssuesSearch API After much debugging I've found a strange issue where in some cases MySQL will return a different result than other enigines if a query is sorted by a null collumn. For example with our integration test data where we don't set updated_unix in repository fixtures: SELECT `id`, `owner_id`, `owner_name`, `lower_name`, `name`, `description`, `website`, `original_service_type`, `original_url`, `default_branch`, `num_watches`, `num_stars`, `num_forks`, `num_issues`, `num_closed_issues`, `num_pulls`, `num_closed_pulls`, `num_milestones`, `num_closed_milestones`, `is_private`, `is_empty`, `is_archived`, `is_mirror`, `status`, `is_fork`, `fork_id`, `is_template`, `template_id`, `size`, `is_fsck_enabled`, `close_issues_via_commit_in_any_branch`, `topics`, `avatar`, `created_unix`, `updated_unix` FROM `repository` ORDER BY updated_unix DESC LIMIT 15 OFFSET 45 Returns different results for MySQL than other engines. However, the similar query: SELECT `id`, `owner_id`, `owner_name`, `lower_name`, `name`, `description`, `website`, `original_service_type`, `original_url`, `default_branch`, `num_watches`, `num_stars`, `num_forks`, `num_issues`, `num_closed_issues`, `num_pulls`, `num_closed_pulls`, `num_milestones`, `num_closed_milestones`, `is_private`, `is_empty`, `is_archived`, `is_mirror`, `status`, `is_fork`, `fork_id`, `is_template`, `template_id`, `size`, `is_fsck_enabled`, `close_issues_via_commit_in_any_branch`, `topics`, `avatar`, `created_unix`, `updated_unix` FROM `repository` ORDER BY updated_unix DESC LIMIT 15 OFFSET 30 Returns the same results. This causes integration tests to fail on MySQL in certain cases but would never show up in a real installation. Since this API call always returns issues based on the optionally provided repo_priority_id or the issueID itself, there is no change to results by changing the repo sorting method used to get ids earlier in the function. * linter is back! * code review * remove now unused option * Fix newline at end of files * more unused code * update to master * check for matching ids before query * Update models/issue_label.go Co-Authored-By: 6543 <6543@obermui.de> * Update models/issue_label.go * update comments * Update routers/org/setting.go Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com> Co-authored-by: 6543 <6543@obermui.de>
5 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
Change target branch for pull request (#6488) * Adds functionality to change target branch of created pull requests Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use const instead of var in JavaScript additions Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Check if branches are equal and if PR already exists before changing target branch Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Make sure to check all commits Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Print error messages for user as error flash message Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Disallow changing target branch of closed or merged pull requests Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Resolve conflicts after merge of upstream/master Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Change order of branch select fields Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes duplicate check Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use ctx.Tr for translations Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Recompile JS Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use correct translation namespace Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Remove redundant if condition Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Moves most change branch logic into pull service Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Completes comment Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Add Ref to ChangesPayload for logging changed target branches instead of creating a new struct Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Revert changes to go.mod Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Directly use createComment method Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Return 404 if pull request is not found. Move written check up Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Remove variable declaration Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Return client errors on change pull request target errors Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Return error in commit.HasPreviousCommit Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adds blank line Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Test patch before persisting new target branch Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Update patch before testing (not working) Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes patch calls when changeing pull request target Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes unneeded check for base name Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Moves ChangeTargetBranch completely to pull service. Update patch status. Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Set webhook mode after errors were validated Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Update PR in one transaction Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Move logic for check if head is equal with branch to pull model Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adds missing comment and simplify return Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adjust CreateComment method call Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com>
5 years ago
3 years ago
Change target branch for pull request (#6488) * Adds functionality to change target branch of created pull requests Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use const instead of var in JavaScript additions Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Check if branches are equal and if PR already exists before changing target branch Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Make sure to check all commits Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Print error messages for user as error flash message Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Disallow changing target branch of closed or merged pull requests Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Resolve conflicts after merge of upstream/master Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Change order of branch select fields Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes duplicate check Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use ctx.Tr for translations Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Recompile JS Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use correct translation namespace Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Remove redundant if condition Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Moves most change branch logic into pull service Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Completes comment Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Add Ref to ChangesPayload for logging changed target branches instead of creating a new struct Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Revert changes to go.mod Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Directly use createComment method Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Return 404 if pull request is not found. Move written check up Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Remove variable declaration Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Return client errors on change pull request target errors Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Return error in commit.HasPreviousCommit Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adds blank line Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Test patch before persisting new target branch Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Update patch before testing (not working) Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes patch calls when changeing pull request target Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes unneeded check for base name Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Moves ChangeTargetBranch completely to pull service. Update patch status. Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Set webhook mode after errors were validated Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Update PR in one transaction Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Move logic for check if head is equal with branch to pull model Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adds missing comment and simplify return Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adjust CreateComment method call Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com>
5 years ago
3 years ago
Change target branch for pull request (#6488) * Adds functionality to change target branch of created pull requests Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use const instead of var in JavaScript additions Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Check if branches are equal and if PR already exists before changing target branch Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Make sure to check all commits Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Print error messages for user as error flash message Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Disallow changing target branch of closed or merged pull requests Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Resolve conflicts after merge of upstream/master Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Change order of branch select fields Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes duplicate check Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use ctx.Tr for translations Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Recompile JS Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use correct translation namespace Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Remove redundant if condition Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Moves most change branch logic into pull service Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Completes comment Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Add Ref to ChangesPayload for logging changed target branches instead of creating a new struct Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Revert changes to go.mod Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Directly use createComment method Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Return 404 if pull request is not found. Move written check up Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Remove variable declaration Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Return client errors on change pull request target errors Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Return error in commit.HasPreviousCommit Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adds blank line Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Test patch before persisting new target branch Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Update patch before testing (not working) Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes patch calls when changeing pull request target Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes unneeded check for base name Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Moves ChangeTargetBranch completely to pull service. Update patch status. Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Set webhook mode after errors were validated Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Update PR in one transaction Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Move logic for check if head is equal with branch to pull model Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adds missing comment and simplify return Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adjust CreateComment method call Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com>
5 years ago
4 years ago
3 years ago
Change target branch for pull request (#6488) * Adds functionality to change target branch of created pull requests Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use const instead of var in JavaScript additions Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Check if branches are equal and if PR already exists before changing target branch Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Make sure to check all commits Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Print error messages for user as error flash message Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Disallow changing target branch of closed or merged pull requests Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Resolve conflicts after merge of upstream/master Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Change order of branch select fields Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes duplicate check Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use ctx.Tr for translations Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Recompile JS Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use correct translation namespace Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Remove redundant if condition Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Moves most change branch logic into pull service Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Completes comment Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Add Ref to ChangesPayload for logging changed target branches instead of creating a new struct Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Revert changes to go.mod Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Directly use createComment method Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Return 404 if pull request is not found. Move written check up Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Remove variable declaration Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Return client errors on change pull request target errors Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Return error in commit.HasPreviousCommit Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adds blank line Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Test patch before persisting new target branch Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Update patch before testing (not working) Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes patch calls when changeing pull request target Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes unneeded check for base name Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Moves ChangeTargetBranch completely to pull service. Update patch status. Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Set webhook mode after errors were validated Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Update PR in one transaction Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Move logic for check if head is equal with branch to pull model Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adds missing comment and simplify return Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adjust CreateComment method call Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com>
5 years ago
3 years ago
Change target branch for pull request (#6488) * Adds functionality to change target branch of created pull requests Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use const instead of var in JavaScript additions Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Check if branches are equal and if PR already exists before changing target branch Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Make sure to check all commits Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Print error messages for user as error flash message Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Disallow changing target branch of closed or merged pull requests Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Resolve conflicts after merge of upstream/master Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Change order of branch select fields Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes duplicate check Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use ctx.Tr for translations Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Recompile JS Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use correct translation namespace Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Remove redundant if condition Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Moves most change branch logic into pull service Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Completes comment Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Add Ref to ChangesPayload for logging changed target branches instead of creating a new struct Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Revert changes to go.mod Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Directly use createComment method Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Return 404 if pull request is not found. Move written check up Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Remove variable declaration Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Return client errors on change pull request target errors Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Return error in commit.HasPreviousCommit Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adds blank line Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Test patch before persisting new target branch Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Update patch before testing (not working) Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes patch calls when changeing pull request target Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes unneeded check for base name Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Moves ChangeTargetBranch completely to pull service. Update patch status. Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Set webhook mode after errors were validated Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Update PR in one transaction Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Move logic for check if head is equal with branch to pull model Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adds missing comment and simplify return Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adjust CreateComment method call Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com>
5 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
Change target branch for pull request (#6488) * Adds functionality to change target branch of created pull requests Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use const instead of var in JavaScript additions Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Check if branches are equal and if PR already exists before changing target branch Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Make sure to check all commits Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Print error messages for user as error flash message Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Disallow changing target branch of closed or merged pull requests Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Resolve conflicts after merge of upstream/master Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Change order of branch select fields Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes duplicate check Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use ctx.Tr for translations Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Recompile JS Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use correct translation namespace Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Remove redundant if condition Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Moves most change branch logic into pull service Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Completes comment Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Add Ref to ChangesPayload for logging changed target branches instead of creating a new struct Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Revert changes to go.mod Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Directly use createComment method Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Return 404 if pull request is not found. Move written check up Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Remove variable declaration Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Return client errors on change pull request target errors Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Return error in commit.HasPreviousCommit Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adds blank line Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Test patch before persisting new target branch Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Update patch before testing (not working) Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes patch calls when changeing pull request target Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes unneeded check for base name Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Moves ChangeTargetBranch completely to pull service. Update patch status. Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Set webhook mode after errors were validated Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Update PR in one transaction Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Move logic for check if head is equal with branch to pull model Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adds missing comment and simplify return Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adjust CreateComment method call Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com>
5 years ago
3 years ago
4 years ago
Change target branch for pull request (#6488) * Adds functionality to change target branch of created pull requests Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use const instead of var in JavaScript additions Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Check if branches are equal and if PR already exists before changing target branch Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Make sure to check all commits Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Print error messages for user as error flash message Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Disallow changing target branch of closed or merged pull requests Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Resolve conflicts after merge of upstream/master Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Change order of branch select fields Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes duplicate check Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use ctx.Tr for translations Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Recompile JS Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use correct translation namespace Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Remove redundant if condition Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Moves most change branch logic into pull service Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Completes comment Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Add Ref to ChangesPayload for logging changed target branches instead of creating a new struct Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Revert changes to go.mod Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Directly use createComment method Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Return 404 if pull request is not found. Move written check up Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Remove variable declaration Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Return client errors on change pull request target errors Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Return error in commit.HasPreviousCommit Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adds blank line Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Test patch before persisting new target branch Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Update patch before testing (not working) Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes patch calls when changeing pull request target Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes unneeded check for base name Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Moves ChangeTargetBranch completely to pull service. Update patch status. Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Set webhook mode after errors were validated Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Update PR in one transaction Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Move logic for check if head is equal with branch to pull model Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adds missing comment and simplify return Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adjust CreateComment method call Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com>
5 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
Add Organization Wide Labels (#10814) * Add organization wide labels Implement organization wide labels similar to organization wide webhooks. This lets you create individual labels for organizations that can be used for all repos under that organization (so being able to reuse the same label across multiple repos). This makes it possible for small organizations with many repos to use labels effectively. Fixes #7406 * Add migration * remove comments * fix tests * Update options/locale/locale_en-US.ini Removed unused translation string * show org labels in issue search label filter * Use more clear var name * rename migration after merge from master * comment typo * update migration again after rebase with master * check for orgID <=0 per guillep2k review * fmt * Apply suggestions from code review Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * remove unused code * Make sure RepoID is 0 when searching orgID per code review * more changes/code review requests * More descriptive translation var per code review * func description/delete comment when issue label deleted instead of hiding it * remove comment * only use issues in that repo when calculating number of open issues for org label on repo label page * Add integration test for IssuesSearch API with labels * remove unused function * Update models/issue_label.go Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * Use subquery in GetLabelIDsInReposByNames * Fix tests to use correct orgID * fix more tests * IssuesSearch api now uses new BuildLabelNamesIssueIDsCondition. Add a few more tests as well * update comment for clarity * Revert previous code change now that we can use the new BuildLabelNamesIssueIDsCondition * Don't sort repos by date in IssuesSearch API After much debugging I've found a strange issue where in some cases MySQL will return a different result than other enigines if a query is sorted by a null collumn. For example with our integration test data where we don't set updated_unix in repository fixtures: SELECT `id`, `owner_id`, `owner_name`, `lower_name`, `name`, `description`, `website`, `original_service_type`, `original_url`, `default_branch`, `num_watches`, `num_stars`, `num_forks`, `num_issues`, `num_closed_issues`, `num_pulls`, `num_closed_pulls`, `num_milestones`, `num_closed_milestones`, `is_private`, `is_empty`, `is_archived`, `is_mirror`, `status`, `is_fork`, `fork_id`, `is_template`, `template_id`, `size`, `is_fsck_enabled`, `close_issues_via_commit_in_any_branch`, `topics`, `avatar`, `created_unix`, `updated_unix` FROM `repository` ORDER BY updated_unix DESC LIMIT 15 OFFSET 45 Returns different results for MySQL than other engines. However, the similar query: SELECT `id`, `owner_id`, `owner_name`, `lower_name`, `name`, `description`, `website`, `original_service_type`, `original_url`, `default_branch`, `num_watches`, `num_stars`, `num_forks`, `num_issues`, `num_closed_issues`, `num_pulls`, `num_closed_pulls`, `num_milestones`, `num_closed_milestones`, `is_private`, `is_empty`, `is_archived`, `is_mirror`, `status`, `is_fork`, `fork_id`, `is_template`, `template_id`, `size`, `is_fsck_enabled`, `close_issues_via_commit_in_any_branch`, `topics`, `avatar`, `created_unix`, `updated_unix` FROM `repository` ORDER BY updated_unix DESC LIMIT 15 OFFSET 30 Returns the same results. This causes integration tests to fail on MySQL in certain cases but would never show up in a real installation. Since this API call always returns issues based on the optionally provided repo_priority_id or the issueID itself, there is no change to results by changing the repo sorting method used to get ids earlier in the function. * linter is back! * code review * remove now unused option * Fix newline at end of files * more unused code * update to master * check for matching ids before query * Update models/issue_label.go Co-Authored-By: 6543 <6543@obermui.de> * Update models/issue_label.go * update comments * Update routers/org/setting.go Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com> Co-authored-by: 6543 <6543@obermui.de>
5 years ago
3 years ago
Add Organization Wide Labels (#10814) * Add organization wide labels Implement organization wide labels similar to organization wide webhooks. This lets you create individual labels for organizations that can be used for all repos under that organization (so being able to reuse the same label across multiple repos). This makes it possible for small organizations with many repos to use labels effectively. Fixes #7406 * Add migration * remove comments * fix tests * Update options/locale/locale_en-US.ini Removed unused translation string * show org labels in issue search label filter * Use more clear var name * rename migration after merge from master * comment typo * update migration again after rebase with master * check for orgID <=0 per guillep2k review * fmt * Apply suggestions from code review Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * remove unused code * Make sure RepoID is 0 when searching orgID per code review * more changes/code review requests * More descriptive translation var per code review * func description/delete comment when issue label deleted instead of hiding it * remove comment * only use issues in that repo when calculating number of open issues for org label on repo label page * Add integration test for IssuesSearch API with labels * remove unused function * Update models/issue_label.go Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * Use subquery in GetLabelIDsInReposByNames * Fix tests to use correct orgID * fix more tests * IssuesSearch api now uses new BuildLabelNamesIssueIDsCondition. Add a few more tests as well * update comment for clarity * Revert previous code change now that we can use the new BuildLabelNamesIssueIDsCondition * Don't sort repos by date in IssuesSearch API After much debugging I've found a strange issue where in some cases MySQL will return a different result than other enigines if a query is sorted by a null collumn. For example with our integration test data where we don't set updated_unix in repository fixtures: SELECT `id`, `owner_id`, `owner_name`, `lower_name`, `name`, `description`, `website`, `original_service_type`, `original_url`, `default_branch`, `num_watches`, `num_stars`, `num_forks`, `num_issues`, `num_closed_issues`, `num_pulls`, `num_closed_pulls`, `num_milestones`, `num_closed_milestones`, `is_private`, `is_empty`, `is_archived`, `is_mirror`, `status`, `is_fork`, `fork_id`, `is_template`, `template_id`, `size`, `is_fsck_enabled`, `close_issues_via_commit_in_any_branch`, `topics`, `avatar`, `created_unix`, `updated_unix` FROM `repository` ORDER BY updated_unix DESC LIMIT 15 OFFSET 45 Returns different results for MySQL than other engines. However, the similar query: SELECT `id`, `owner_id`, `owner_name`, `lower_name`, `name`, `description`, `website`, `original_service_type`, `original_url`, `default_branch`, `num_watches`, `num_stars`, `num_forks`, `num_issues`, `num_closed_issues`, `num_pulls`, `num_closed_pulls`, `num_milestones`, `num_closed_milestones`, `is_private`, `is_empty`, `is_archived`, `is_mirror`, `status`, `is_fork`, `fork_id`, `is_template`, `template_id`, `size`, `is_fsck_enabled`, `close_issues_via_commit_in_any_branch`, `topics`, `avatar`, `created_unix`, `updated_unix` FROM `repository` ORDER BY updated_unix DESC LIMIT 15 OFFSET 30 Returns the same results. This causes integration tests to fail on MySQL in certain cases but would never show up in a real installation. Since this API call always returns issues based on the optionally provided repo_priority_id or the issueID itself, there is no change to results by changing the repo sorting method used to get ids earlier in the function. * linter is back! * code review * remove now unused option * Fix newline at end of files * more unused code * update to master * check for matching ids before query * Update models/issue_label.go Co-Authored-By: 6543 <6543@obermui.de> * Update models/issue_label.go * update comments * Update routers/org/setting.go Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com> Co-authored-by: 6543 <6543@obermui.de>
5 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
Add single sign-on support via SSPI on Windows (#8463) * Add single sign-on support via SSPI on Windows * Ensure plugins implement interface * Ensure plugins implement interface * Move functions used only by the SSPI auth method to sspi_windows.go * Field SSPISeparatorReplacement of AuthenticationForm should not be required via binding, as binding will insist the field is non-empty even if another login type is selected * Fix breaking of oauth authentication on download links. Do not create new session with SSPI authentication on download links. * Update documentation for the new 'SPNEGO with SSPI' login source * Mention in documentation that ROOT_URL should contain the FQDN of the server * Make sure that Contexter is not checking for active login sources when the ORM engine is not initialized (eg. when installing) * Always initialize and free SSO methods, even if they are not enabled, as a method can be activated while the app is running (from Authentication sources) * Add option in SSPIConfig for removing of domains from logon names * Update helper text for StripDomainNames option * Make sure handleSignIn() is called after a new user object is created by SSPI auth method * Remove default value from text of form field helper Co-Authored-By: Lauris BH <lauris@nix.lv> * Remove default value from text of form field helper Co-Authored-By: Lauris BH <lauris@nix.lv> * Remove default value from text of form field helper Co-Authored-By: Lauris BH <lauris@nix.lv> * Only make a query to the DB to check if SSPI is enabled on handlers that need that information for templates * Remove code duplication * Log errors in ActiveLoginSources Co-Authored-By: Lauris BH <lauris@nix.lv> * Revert suffix of randomly generated E-mails for Reverse proxy authentication Co-Authored-By: Lauris BH <lauris@nix.lv> * Revert unneeded white-space change in template Co-Authored-By: Lauris BH <lauris@nix.lv> * Add copyright comments at the top of new files * Use loopback name for randomly generated emails * Add locale tag for the SSPISeparatorReplacement field with proper casing * Revert casing of SSPISeparatorReplacement field in locale file, moving it up, next to other form fields * Update docs/content/doc/features/authentication.en-us.md Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * Remove Priority() method and define the order in which SSO auth methods should be executed in one place * Log authenticated username only if it's not empty * Rephrase helper text for automatic creation of users * Return error if more than one active SSPI auth source is found * Change newUser() function to return error, letting caller log/handle the error * Move isPublicResource, isPublicPage and handleSignIn functions outside SSPI auth method to allow other SSO methods to reuse them if needed * Refactor initialization of the list containing SSO auth methods * Validate SSPI settings on POST * Change SSPI to only perform authentication on its own login page, API paths and download links. Leave Toggle middleware to redirect non authenticated users to login page * Make 'Default language' in SSPI config empty, unless changed by admin * Show error if admin tries to add a second authentication source of type SSPI * Simplify declaration of global variable * Rebuild gitgraph.js on Linux * Make sure config values containing only whitespace are not accepted
5 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
Use AJAX for notifications table (#10961) * Use AJAX for notifications table Signed-off-by: Andrew Thornton <art27@cantab.net> * move to separate js Signed-off-by: Andrew Thornton <art27@cantab.net> * placate golangci-lint Signed-off-by: Andrew Thornton <art27@cantab.net> * Add autoupdating notification count Signed-off-by: Andrew Thornton <art27@cantab.net> * Fix wipeall Signed-off-by: Andrew Thornton <art27@cantab.net> * placate tests Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hide and hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * More auto-update improvements Only run checker on pages that have a count Change starting checker to 10s with a back-off to 60s if there is no change Signed-off-by: Andrew Thornton <art27@cantab.net> * string comparison! Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @silverwind Signed-off-by: Andrew Thornton <art27@cantab.net> * add configurability as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Add documentation as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Use CSRF header not query Signed-off-by: Andrew Thornton <art27@cantab.net> * Further JS improvements Fix @etzelia update notification table request Fix @silverwind comments Co-Authored-By: silverwind <me@silverwind.io> Signed-off-by: Andrew Thornton <art27@cantab.net> * Simplify the notification count fns Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: silverwind <me@silverwind.io>
5 years ago
3 years ago
Use AJAX for notifications table (#10961) * Use AJAX for notifications table Signed-off-by: Andrew Thornton <art27@cantab.net> * move to separate js Signed-off-by: Andrew Thornton <art27@cantab.net> * placate golangci-lint Signed-off-by: Andrew Thornton <art27@cantab.net> * Add autoupdating notification count Signed-off-by: Andrew Thornton <art27@cantab.net> * Fix wipeall Signed-off-by: Andrew Thornton <art27@cantab.net> * placate tests Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hide and hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * More auto-update improvements Only run checker on pages that have a count Change starting checker to 10s with a back-off to 60s if there is no change Signed-off-by: Andrew Thornton <art27@cantab.net> * string comparison! Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @silverwind Signed-off-by: Andrew Thornton <art27@cantab.net> * add configurability as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Add documentation as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Use CSRF header not query Signed-off-by: Andrew Thornton <art27@cantab.net> * Further JS improvements Fix @etzelia update notification table request Fix @silverwind comments Co-Authored-By: silverwind <me@silverwind.io> Signed-off-by: Andrew Thornton <art27@cantab.net> * Simplify the notification count fns Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: silverwind <me@silverwind.io>
5 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
Use AJAX for notifications table (#10961) * Use AJAX for notifications table Signed-off-by: Andrew Thornton <art27@cantab.net> * move to separate js Signed-off-by: Andrew Thornton <art27@cantab.net> * placate golangci-lint Signed-off-by: Andrew Thornton <art27@cantab.net> * Add autoupdating notification count Signed-off-by: Andrew Thornton <art27@cantab.net> * Fix wipeall Signed-off-by: Andrew Thornton <art27@cantab.net> * placate tests Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hide and hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * More auto-update improvements Only run checker on pages that have a count Change starting checker to 10s with a back-off to 60s if there is no change Signed-off-by: Andrew Thornton <art27@cantab.net> * string comparison! Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @silverwind Signed-off-by: Andrew Thornton <art27@cantab.net> * add configurability as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Add documentation as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Use CSRF header not query Signed-off-by: Andrew Thornton <art27@cantab.net> * Further JS improvements Fix @etzelia update notification table request Fix @silverwind comments Co-Authored-By: silverwind <me@silverwind.io> Signed-off-by: Andrew Thornton <art27@cantab.net> * Simplify the notification count fns Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: silverwind <me@silverwind.io>
5 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
2 years ago
3 years ago
3 years ago
Use AJAX for notifications table (#10961) * Use AJAX for notifications table Signed-off-by: Andrew Thornton <art27@cantab.net> * move to separate js Signed-off-by: Andrew Thornton <art27@cantab.net> * placate golangci-lint Signed-off-by: Andrew Thornton <art27@cantab.net> * Add autoupdating notification count Signed-off-by: Andrew Thornton <art27@cantab.net> * Fix wipeall Signed-off-by: Andrew Thornton <art27@cantab.net> * placate tests Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hide and hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * More auto-update improvements Only run checker on pages that have a count Change starting checker to 10s with a back-off to 60s if there is no change Signed-off-by: Andrew Thornton <art27@cantab.net> * string comparison! Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @silverwind Signed-off-by: Andrew Thornton <art27@cantab.net> * add configurability as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Add documentation as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Use CSRF header not query Signed-off-by: Andrew Thornton <art27@cantab.net> * Further JS improvements Fix @etzelia update notification table request Fix @silverwind comments Co-Authored-By: silverwind <me@silverwind.io> Signed-off-by: Andrew Thornton <art27@cantab.net> * Simplify the notification count fns Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: silverwind <me@silverwind.io>
5 years ago
Support unicode emojis and remove emojify.js (#11032) * Support unicode emojis and remove emojify.js This PR replaces all use of emojify.js and adds unicode emoji support to various areas of gitea. This works in a few ways: First it adds emoji parsing support into gitea itself. This allows us to * Render emojis from valid alias (:smile:) * Detect unicode emojis and let us put them in their own class with proper aria-labels and styling * Easily allow for custom "emoji" * Support all emoji rendering and features without javascript * Uses plain unicode and lets the system render in appropriate emoji font * Doesn't leave us relying on external sources for updates/fixes/features That same list of emoji is also used to create a json file which replaces the part of emojify.js that populates the emoji search tribute. This file is about 35KB with GZIP turned on and I've set it to load after the page renders to not hinder page load time (and this removes loading emojify.js also) For custom "emoji" it uses a pretty simple scheme of just looking for /emojis/img/name.png where name is something a user has put in the "allowed reactions" setting we already have. The gitea reaction that was previously hard coded into a forked copy of emojify.js is included and works as a custom reaction under this method. The emoji data sourced here is from https://github.com/github/gemoji which is the gem library Github uses for their emoji rendering (and a data source for other sites). So we should be able to easily render any emoji and :alias: that Github can, removing any errors from migrated content. They also update it as well, so we can sync when there are new unicode emoji lists released. I've included a slimmed down and slightly modified forked copy of https://github.com/knq/emoji to make up our own emoji module. The code is pretty straight forward and again allows us to have a lot of flexibility in what happens. I had seen a few comments about performance in some of the other threads if we render this ourselves, but there doesn't seem to be any issue here. In a test it can parse, convert, and render 1,000 emojis inside of a large markdown table in about 100ms on my laptop (which is many more emojis than will ever be in any normal issue). This also prevents any flickering and other weirdness from using javascript to render some things while using go for others. Not included here are image fall back URLS. I don't really think they are necessary for anything new being written in 2020. However, managing the emoji ourselves would allow us to add these as a feature later on if it seems necessary. Fixes: https://github.com/go-gitea/gitea/issues/9182 Fixes: https://github.com/go-gitea/gitea/issues/8974 Fixes: https://github.com/go-gitea/gitea/issues/8953 Fixes: https://github.com/go-gitea/gitea/issues/6628 Fixes: https://github.com/go-gitea/gitea/issues/5130 * add new shared function emojiHTML * don't increase emoji size in issue title * Update templates/repo/issue/view_content/add_reaction.tmpl Co-Authored-By: 6543 <6543@obermui.de> * Support for emoji rendering in various templates * Render code and review comments as they should be * Better way to handle mail subjects * insert unicode from tribute selection * Add template helper for plain text when needed * Use existing replace function I forgot about * Don't include emoji greater than Unicode Version 12 Only include emoji and aliases in JSON * Update build/generate-emoji.go * Tweak regex slightly to really match everything including random invisible characters. Run tests for every emoji we have * final updates * code review * code review * hard code gitea custom emoji to match previous behavior * Update .eslintrc Co-Authored-By: silverwind <me@silverwind.io> * disable preempt Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com>
5 years ago
3 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
2 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
2 years ago
3 years ago
3 years ago
2 years ago
3 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
2 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
2 years ago
3 years ago
2 years ago
3 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231
  1. /* globals wipPrefixes */
  2. /* exported timeAddManual, toggleStopwatch, cancelStopwatch */
  3. /* exported toggleDeadlineForm, setDeadline, updateDeadline, deleteDependencyModal, cancelCodeComment, onOAuthLoginClick */
  4. import "./publicpath.js";
  5. import "./polyfills.js";
  6. import "./features/letteravatar.js";
  7. import Vue from "vue";
  8. import "element-ui/lib/theme-chalk/index.css";
  9. import localeEn from 'element-ui/lib/locale/lang/en';
  10. import localeZh from 'element-ui/lib/locale/lang/zh-CN';
  11. import ElementUI from "element-ui";
  12. import axios from "axios";
  13. import qs from "qs";
  14. import Cookies from "js-cookie";
  15. import "jquery.are-you-sure";
  16. import "./vendor/semanticdropdown.js";
  17. import { svg } from "./utils.js";
  18. import echarts from "echarts";
  19. import initContextPopups from "./features/contextpopup.js";
  20. import initGitGraph from "./features/gitgraph.js";
  21. import initClipboard from "./features/clipboard.js";
  22. import initUserHeatmap from "./features/userheatmap.js";
  23. import initDateTimePicker from "./features/datetimepicker.js";
  24. import initContextMenu from "./features/contexmenu.js";
  25. import {
  26. initTribute,
  27. issuesTribute,
  28. emojiTribute,
  29. } from "./features/tribute.js";
  30. import createDropzone from "./features/dropzone.js";
  31. import highlight from "./features/highlight.js";
  32. import ActivityTopAuthors from "./components/ActivityTopAuthors.vue";
  33. import {
  34. initNotificationsTable,
  35. initNotificationCount,
  36. } from "./features/notification.js";
  37. import { createCodeEditor } from "./features/codeeditor.js";
  38. import MinioUploader from "./components/MinioUploader.vue";
  39. import EditAboutInfo from "./components/EditAboutInfo.vue";
  40. // import Images from './components/Images.vue';
  41. import EditTopics from "./components/EditTopics.vue";
  42. import DataAnalysis from "./components/DataAnalysis.vue";
  43. import Contributors from "./components/Contributors.vue";
  44. import Model from "./components/Model.vue";
  45. import WxAutorize from "./components/WxAutorize.vue";
  46. import initCloudrain from "./features/cloudrbanin.js";
  47. import initCloudrainSow from "./features/cloudbrainShow.js";
  48. import initImage from "./features/images.js";
  49. import selectDataset from "./components/dataset/selectDataset.vue";
  50. import referenceDataset from "./components/dataset/referenceDataset.vue";
  51. // import $ from 'jquery.js'
  52. // import router from "./router/index.js";
  53. import { Message } from "element-ui";
  54. import { i18nVue } from "./features/i18nVue.js";
  55. import './features/ad.js';
  56. import { Fancybox } from "./vendor/fancybox.esm.js";
  57. Vue.prototype.$axios = axios;
  58. Vue.prototype.$Cookies = Cookies;
  59. Vue.prototype.qs = qs;
  60. Vue.prototype.$message = Message;
  61. Vue.prototype.$locale = i18nVue;
  62. const lang = document.querySelector('html').getAttribute('lang');
  63. window.i18n = i18nVue[lang == 'zh-CN' ? 'CN' : 'US'];
  64. const { AppSubUrl, StaticUrlPrefix, csrf } = window.config;
  65. Vue.use(ElementUI, {
  66. locale: lang === 'zh-CN' ? localeZh : localeEn,
  67. });
  68. Object.defineProperty(Vue.prototype, "$echarts", {
  69. value: echarts,
  70. });
  71. window.echarts = echarts;
  72. function htmlEncode(text) {
  73. return jQuery("<div />").text(text).html();
  74. }
  75. let previewFileModes;
  76. const commentMDEditors = {};
  77. // Silence fomantic's error logging when tabs are used without a target content element
  78. $.fn.tab.settings.silent = true;
  79. function initCommentPreviewTab($form) {
  80. const $tabMenu = $form.find(".tabular.menu");
  81. $tabMenu.find(".item").tab();
  82. $tabMenu
  83. .find(`.item[data-tab="${$tabMenu.data("preview")}"]`)
  84. .on("click", function () {
  85. const $this = $(this);
  86. $.post(
  87. $this.data("url"),
  88. {
  89. _csrf: csrf,
  90. mode: "gfm",
  91. context: $this.data("context"),
  92. text: $form
  93. .find(`.tab[data-tab="${$tabMenu.data("write")}"] textarea`)
  94. .val(),
  95. },
  96. (data) => {
  97. const $previewPanel = $form.find(
  98. `.tab[data-tab="${$tabMenu.data("preview")}"]`
  99. );
  100. $previewPanel.html(data);
  101. $("pre code", $previewPanel[0]).each(function () {
  102. highlight(this);
  103. });
  104. }
  105. );
  106. });
  107. buttonsClickOnEnter();
  108. }
  109. function initEditPreviewTab($form) {
  110. const $tabMenu = $form.find(".tabular.menu");
  111. $tabMenu.find(".item").tab();
  112. const $previewTab = $tabMenu.find(
  113. `.item[data-tab="${$tabMenu.data("preview")}"]`
  114. );
  115. if ($previewTab.length) {
  116. previewFileModes = $previewTab.data("preview-file-modes").split(",");
  117. $previewTab.on("click", function () {
  118. const $this = $(this);
  119. let context = `${$this.data("context")}/`;
  120. const treePathEl = $form.find("input#tree_path");
  121. if (treePathEl.length > 0) {
  122. context += treePathEl.val();
  123. }
  124. context = context.substring(0, context.lastIndexOf("/"));
  125. $.post(
  126. $this.data("url"),
  127. {
  128. _csrf: csrf,
  129. mode: "gfm",
  130. context,
  131. text: $form
  132. .find(`.tab[data-tab="${$tabMenu.data("write")}"] textarea`)
  133. .val(),
  134. },
  135. (data) => {
  136. const $previewPanel = $form.find(
  137. `.tab[data-tab="${$tabMenu.data("preview")}"]`
  138. );
  139. $previewPanel.html(data);
  140. $("pre code", $previewPanel[0]).each(function () {
  141. highlight(this);
  142. });
  143. }
  144. );
  145. });
  146. }
  147. }
  148. function initEditDiffTab($form) {
  149. const $tabMenu = $form.find(".tabular.menu");
  150. $tabMenu.find(".item").tab();
  151. $tabMenu
  152. .find(`.item[data-tab="${$tabMenu.data("diff")}"]`)
  153. .on("click", function () {
  154. const $this = $(this);
  155. $.post(
  156. $this.data("url"),
  157. {
  158. _csrf: csrf,
  159. context: $this.data("context"),
  160. content: $form
  161. .find(`.tab[data-tab="${$tabMenu.data("write")}"] textarea`)
  162. .val(),
  163. },
  164. (data) => {
  165. const $diffPreviewPanel = $form.find(
  166. `.tab[data-tab="${$tabMenu.data("diff")}"]`
  167. );
  168. $diffPreviewPanel.html(data);
  169. }
  170. );
  171. });
  172. }
  173. function initEditForm() {
  174. if ($(".edit.form").length === 0) {
  175. return;
  176. }
  177. initEditPreviewTab($(".edit.form"));
  178. initEditDiffTab($(".edit.form"));
  179. }
  180. function initBranchSelector() {
  181. const $selectBranch = $(".ui.select-branch");
  182. const $branchMenu = $selectBranch.find(".reference-list-menu");
  183. $branchMenu.find(".item:not(.no-select)").click(function () {
  184. $($(this).data("id-selector")).val($(this).data("id"));
  185. $selectBranch.find(".ui .branch-name").text($(this).data("name"));
  186. });
  187. $selectBranch.find(".reference.column").on("click", function () {
  188. $selectBranch.find(".scrolling.reference-list-menu").css("display", "none");
  189. $selectBranch.find(".reference .text").addClass("black");
  190. $($(this).data("target")).css("display", "block");
  191. $(this).find(".text.black").removeClass("black");
  192. return false;
  193. });
  194. }
  195. function initLabelEdit() {
  196. // Create label
  197. const $newLabelPanel = $(".new-label.segment");
  198. $(".new-label.button").on("click", () => {
  199. $newLabelPanel.show();
  200. });
  201. $(".new-label.segment .cancel").on("click", () => {
  202. $newLabelPanel.hide();
  203. });
  204. $(".color-picker").each(function () {
  205. $(this).minicolors();
  206. });
  207. $(".precolors .color").on("click", function () {
  208. const color_hex = $(this).data("color-hex");
  209. $(".color-picker").val(color_hex);
  210. $(".minicolors-swatch-color").css("background-color", color_hex);
  211. });
  212. $(".edit-label-button").on("click", function () {
  213. $("#label-modal-id").val($(this).data("id"));
  214. $(".edit-label .new-label-input").val($(this).data("title"));
  215. $(".edit-label .new-label-desc-input").val($(this).data("description"));
  216. $(".edit-label .color-picker").val($(this).data("color"));
  217. $(".minicolors-swatch-color").css(
  218. "background-color",
  219. $(this).data("color")
  220. );
  221. $(".edit-label.modal")
  222. .modal({
  223. onApprove() {
  224. $(".edit-label.form").trigger("submit");
  225. },
  226. })
  227. .modal("show");
  228. return false;
  229. });
  230. }
  231. function updateIssuesMeta(url, action, issueIds, elementId, isAdd) {
  232. return new Promise((resolve) => {
  233. $.ajax({
  234. type: "POST",
  235. url,
  236. data: {
  237. _csrf: csrf,
  238. action,
  239. issue_ids: issueIds,
  240. id: elementId,
  241. is_add: isAdd,
  242. },
  243. success: resolve,
  244. });
  245. });
  246. }
  247. function initRepoStatusChecker() {
  248. const migrating = $("#repo_migrating");
  249. $("#repo_migrating_failed").hide();
  250. if (migrating) {
  251. const repo_name = migrating.attr("repo");
  252. if (typeof repo_name === "undefined") {
  253. return;
  254. }
  255. $.ajax({
  256. type: "GET",
  257. url: `${AppSubUrl}/${repo_name}/status`,
  258. data: {
  259. _csrf: csrf,
  260. },
  261. complete(xhr) {
  262. if (xhr.status === 200) {
  263. if (xhr.responseJSON) {
  264. if (xhr.responseJSON.status === 0) {
  265. window.location.reload();
  266. return;
  267. }
  268. setTimeout(() => {
  269. initRepoStatusChecker();
  270. }, 2000);
  271. return;
  272. }
  273. }
  274. $("#repo_migrating_progress").hide();
  275. $("#repo_migrating_failed").show();
  276. },
  277. });
  278. }
  279. }
  280. function initReactionSelector(parent) {
  281. let reactions = "";
  282. if (!parent) {
  283. parent = $(document);
  284. reactions = ".reactions > ";
  285. }
  286. parent.find(`${reactions}a.label`).popup({
  287. position: "bottom left",
  288. metadata: { content: "title", title: "none" },
  289. });
  290. parent
  291. .find(`.select-reaction > .menu > .item, ${reactions}a.label`)
  292. .on("click", function (e) {
  293. const vm = this;
  294. e.preventDefault();
  295. if ($(this).hasClass("disabled")) return;
  296. const actionURL = $(this).hasClass("item")
  297. ? $(this).closest(".select-reaction").data("action-url")
  298. : $(this).data("action-url");
  299. const url = `${actionURL}/${
  300. $(this).hasClass("blue") ? "unreact" : "react"
  301. }`;
  302. $.ajax({
  303. type: "POST",
  304. url,
  305. data: {
  306. _csrf: csrf,
  307. content: $(this).data("content"),
  308. },
  309. }).done((resp) => {
  310. if (resp && (resp.html || resp.empty)) {
  311. const content = $(vm).closest(".content");
  312. let react = content.find(".segment.reactions");
  313. if (!resp.empty && react.length > 0) {
  314. react.remove();
  315. }
  316. if (!resp.empty) {
  317. react = $('<div class="ui attached segment reactions"></div>');
  318. const attachments = content.find(".segment.bottom:first");
  319. if (attachments.length > 0) {
  320. react.insertBefore(attachments);
  321. } else {
  322. react.appendTo(content);
  323. }
  324. react.html(resp.html);
  325. react.find(".dropdown").dropdown();
  326. initReactionSelector(react);
  327. }
  328. }
  329. });
  330. });
  331. }
  332. function insertAtCursor(field, value) {
  333. if (field.selectionStart || field.selectionStart === 0) {
  334. const startPos = field.selectionStart;
  335. const endPos = field.selectionEnd;
  336. field.value =
  337. field.value.substring(0, startPos) +
  338. value +
  339. field.value.substring(endPos, field.value.length);
  340. field.selectionStart = startPos + value.length;
  341. field.selectionEnd = startPos + value.length;
  342. } else {
  343. field.value += value;
  344. }
  345. }
  346. function replaceAndKeepCursor(field, oldval, newval) {
  347. if (field.selectionStart || field.selectionStart === 0) {
  348. const startPos = field.selectionStart;
  349. const endPos = field.selectionEnd;
  350. field.value = field.value.replace(oldval, newval);
  351. field.selectionStart = startPos + newval.length - oldval.length;
  352. field.selectionEnd = endPos + newval.length - oldval.length;
  353. } else {
  354. field.value = field.value.replace(oldval, newval);
  355. }
  356. }
  357. function retrieveImageFromClipboardAsBlob(pasteEvent, callback) {
  358. if (!pasteEvent.clipboardData) {
  359. return;
  360. }
  361. const { items } = pasteEvent.clipboardData;
  362. if (typeof items === "undefined") {
  363. return;
  364. }
  365. for (let i = 0; i < items.length; i++) {
  366. if (!items[i].type.includes("image")) continue;
  367. const blob = items[i].getAsFile();
  368. if (typeof callback === "function") {
  369. pasteEvent.preventDefault();
  370. pasteEvent.stopPropagation();
  371. callback(blob);
  372. }
  373. }
  374. }
  375. function uploadFile(file, callback) {
  376. const xhr = new XMLHttpRequest();
  377. xhr.addEventListener("load", () => {
  378. if (xhr.status === 200) {
  379. callback(xhr.responseText);
  380. }
  381. });
  382. xhr.open("post", `${AppSubUrl}/attachments`, true);
  383. xhr.setRequestHeader("X-Csrf-Token", csrf);
  384. const formData = new FormData();
  385. formData.append("file", file, file.name);
  386. xhr.send(formData);
  387. }
  388. function reload() {
  389. window.location.reload();
  390. }
  391. function initImagePaste(target) {
  392. target.each(function () {
  393. const field = this;
  394. field.addEventListener(
  395. "paste",
  396. (event) => {
  397. retrieveImageFromClipboardAsBlob(event, (img) => {
  398. const name = img.name.substr(0, img.name.lastIndexOf("."));
  399. insertAtCursor(field, `![${name}]()`);
  400. uploadFile(img, (res) => {
  401. const data = JSON.parse(res);
  402. replaceAndKeepCursor(
  403. field,
  404. `![${name}]()`,
  405. `![${name}](${AppSubUrl}/attachments/${data.uuid})`
  406. );
  407. const input = $(
  408. `<input id="${data.uuid}" name="files" type="hidden">`
  409. ).val(data.uuid);
  410. $(".files").append(input);
  411. });
  412. });
  413. },
  414. false
  415. );
  416. });
  417. }
  418. function initSimpleMDEImagePaste(simplemde, files) {
  419. simplemde.codemirror.on("paste", (_, event) => {
  420. retrieveImageFromClipboardAsBlob(event, (img) => {
  421. const name = img.name.substr(0, img.name.lastIndexOf("."));
  422. uploadFile(img, (res) => {
  423. const data = JSON.parse(res);
  424. const pos = simplemde.codemirror.getCursor();
  425. simplemde.codemirror.replaceRange(
  426. `![${name}](${AppSubUrl}/attachments/${data.uuid})`,
  427. pos
  428. );
  429. const input = $(
  430. `<input id="${data.uuid}" name="files" type="hidden">`
  431. ).val(data.uuid);
  432. files.append(input);
  433. });
  434. });
  435. });
  436. }
  437. let autoSimpleMDE;
  438. function initCommentForm() {
  439. if ($(".comment.form").length === 0) {
  440. return;
  441. }
  442. autoSimpleMDE = setCommentSimpleMDE(
  443. $(".comment.form textarea:not(.review-textarea)")
  444. );
  445. initBranchSelector();
  446. initCommentPreviewTab($(".comment.form"));
  447. initImagePaste($(".comment.form textarea"));
  448. // Listsubmit
  449. function initListSubmits(selector, outerSelector) {
  450. const $list = $(`.ui.${outerSelector}.list`);
  451. const $noSelect = $list.find(".no-select");
  452. const $listMenu = $(`.${selector} .menu`);
  453. let hasLabelUpdateAction = $listMenu.data("action") === "update";
  454. const labels = {};
  455. $(`.${selector}`).dropdown("setting", "onHide", () => {
  456. hasLabelUpdateAction = $listMenu.data("action") === "update"; // Update the var
  457. if (hasLabelUpdateAction) {
  458. const promises = [];
  459. Object.keys(labels).forEach((elementId) => {
  460. const label = labels[elementId];
  461. console.log("label:", label);
  462. const promise = updateIssuesMeta(
  463. label["update-url"],
  464. label.action,
  465. label["issue-id"],
  466. elementId,
  467. label["is-checked"]
  468. );
  469. promises.push(promise);
  470. });
  471. Promise.all(promises).then(reload);
  472. }
  473. });
  474. $listMenu.find(".item:not(.no-select)").on("click", function () {
  475. // we don't need the action attribute when updating assignees
  476. if (
  477. selector === "select-assignees-modify" ||
  478. selector === "select-reviewers-modify"
  479. ) {
  480. // UI magic. We need to do this here, otherwise it would destroy the functionality of
  481. // adding/removing labels
  482. if ($(this).data("can-change") === "block") {
  483. return false;
  484. }
  485. if ($(this).hasClass("checked")) {
  486. $(this).removeClass("checked");
  487. $(this).find(".octicon-check").addClass("invisible");
  488. $(this).data("is-checked", "remove");
  489. } else {
  490. $(this).addClass("checked");
  491. $(this).find(".octicon-check").removeClass("invisible");
  492. $(this).data("is-checked", "add");
  493. }
  494. updateIssuesMeta(
  495. $listMenu.data("update-url"),
  496. "",
  497. $listMenu.data("issue-id"),
  498. $(this).data("id"),
  499. $(this).data("is-checked")
  500. );
  501. $listMenu.data("action", "update"); // Update to reload the page when we updated items
  502. return false;
  503. }
  504. if ($(this).hasClass("checked")) {
  505. $(this).removeClass("checked");
  506. $(this).find(".octicon-check").addClass("invisible");
  507. if (hasLabelUpdateAction) {
  508. if (!($(this).data("id") in labels)) {
  509. labels[$(this).data("id")] = {
  510. "update-url": $listMenu.data("update-url"),
  511. action: "detach",
  512. "issue-id": $listMenu.data("issue-id"),
  513. };
  514. } else {
  515. delete labels[$(this).data("id")];
  516. }
  517. }
  518. } else {
  519. $(this).addClass("checked");
  520. $(this).find(".octicon-check").removeClass("invisible");
  521. if (hasLabelUpdateAction) {
  522. if (!($(this).data("id") in labels)) {
  523. labels[$(this).data("id")] = {
  524. "update-url": $listMenu.data("update-url"),
  525. action: "attach",
  526. "issue-id": $listMenu.data("issue-id"),
  527. };
  528. } else {
  529. delete labels[$(this).data("id")];
  530. }
  531. }
  532. }
  533. const listIds = [];
  534. $(this)
  535. .parent()
  536. .find(".item")
  537. .each(function () {
  538. if ($(this).hasClass("checked")) {
  539. listIds.push($(this).data("id"));
  540. $($(this).data("id-selector")).removeClass("hide");
  541. } else {
  542. $($(this).data("id-selector")).addClass("hide");
  543. }
  544. });
  545. if (listIds.length === 0) {
  546. $noSelect.removeClass("hide");
  547. } else {
  548. $noSelect.addClass("hide");
  549. }
  550. $($(this).parent().data("id")).val(listIds.join(","));
  551. return false;
  552. });
  553. $listMenu.find(".no-select.item").on("click", function () {
  554. if (hasLabelUpdateAction || selector === "select-assignees-modify") {
  555. updateIssuesMeta(
  556. $listMenu.data("update-url"),
  557. "clear",
  558. $listMenu.data("issue-id"),
  559. "",
  560. ""
  561. ).then(reload);
  562. }
  563. $(this)
  564. .parent()
  565. .find(".item")
  566. .each(function () {
  567. $(this).removeClass("checked");
  568. $(this).find(".octicon").addClass("invisible");
  569. $(this).data("is-checked", "remove");
  570. });
  571. $list.find(".item").each(function () {
  572. $(this).addClass("hide");
  573. });
  574. $noSelect.removeClass("hide");
  575. $($(this).parent().data("id")).val("");
  576. });
  577. }
  578. // Init labels and assignees
  579. initListSubmits("select-label", "labels");
  580. initListSubmits("select-assignees", "assignees");
  581. initListSubmits("select-assignees-modify", "assignees");
  582. initListSubmits("select-reviewers-modify", "assignees");
  583. function selectItem(select_id, input_id) {
  584. let $menu;
  585. if (select_id == ".select-branch") {
  586. $menu = $(`${select_id} .menu`).eq(1);
  587. } else {
  588. $menu = $(`${select_id} .menu`);
  589. }
  590. const $list = $(`.ui${select_id}.list`);
  591. const hasUpdateAction = $menu.data("action") === "update";
  592. $menu.find(".item:not(.no-select)").on("click", function () {
  593. $(this)
  594. .parent()
  595. .find(".item")
  596. .each(function () {
  597. $(this).removeClass("selected active");
  598. });
  599. $(this).addClass("selected active");
  600. if (hasUpdateAction) {
  601. //let ref = ''
  602. //if (select_id=='.select-branch'){
  603. // ref = $(this).data('name');
  604. // }
  605. updateIssuesMeta(
  606. $menu.data("update-url"),
  607. "",
  608. $menu.data("issue-id"),
  609. $(this).data("id"),
  610. $(this).data("is-checked")
  611. ).then(reload);
  612. }
  613. switch (input_id) {
  614. case "#milestone_id":
  615. $list
  616. .find(".selected")
  617. .html(
  618. `<a class="item" href=${$(this).data("href")}>${htmlEncode(
  619. $(this).text()
  620. )}</a>`
  621. );
  622. break;
  623. case "#assignee_id":
  624. $list
  625. .find(".selected")
  626. .html(
  627. `<a class="item" href=${$(this).data("href")}>` +
  628. `<img class="ui avatar image" src=${$(this).data(
  629. "avatar"
  630. )}>${htmlEncode($(this).text())}</a>`
  631. );
  632. }
  633. $(`.ui${select_id}.list .no-select`).addClass("hide");
  634. $(input_id).val($(this).data("id"));
  635. });
  636. $menu.find(".no-select.item").on("click", function () {
  637. $(this)
  638. .parent()
  639. .find(".item:not(.no-select)")
  640. .each(function () {
  641. $(this).removeClass("selected active");
  642. });
  643. if (hasUpdateAction) {
  644. updateIssuesMeta(
  645. $menu.data("update-url"),
  646. "",
  647. $menu.data("issue-id"),
  648. $(this).data("id"),
  649. $(this).data("is-checked")
  650. ).then(reload);
  651. }
  652. $list.find(".selected").html("");
  653. $list.find(".no-select").removeClass("hide");
  654. $(input_id).val("");
  655. });
  656. }
  657. // Milestone and assignee
  658. selectItem(".select-milestone", "#milestone_id");
  659. selectItem(".select-assignee", "#assignee_id");
  660. selectItem(".select-branch", "");
  661. }
  662. function initInstall() {
  663. if ($(".install").length === 0) {
  664. return;
  665. }
  666. if ($("#db_host").val() === "") {
  667. $("#db_host").val("127.0.0.1:3306");
  668. $("#db_user").val("gitea");
  669. $("#db_name").val("gitea");
  670. }
  671. // Database type change detection.
  672. $("#db_type").on("change", function () {
  673. const sqliteDefault = "data/gitea.db";
  674. const tidbDefault = "data/gitea_tidb";
  675. const dbType = $(this).val();
  676. if (dbType === "SQLite3") {
  677. $("#sql_settings").hide();
  678. $("#pgsql_settings").hide();
  679. $("#mysql_settings").hide();
  680. $("#sqlite_settings").show();
  681. if (dbType === "SQLite3" && $("#db_path").val() === tidbDefault) {
  682. $("#db_path").val(sqliteDefault);
  683. }
  684. return;
  685. }
  686. const dbDefaults = {
  687. MySQL: "127.0.0.1:3306",
  688. PostgreSQL: "127.0.0.1:5432",
  689. MSSQL: "127.0.0.1:1433",
  690. };
  691. $("#sqlite_settings").hide();
  692. $("#sql_settings").show();
  693. $("#pgsql_settings").toggle(dbType === "PostgreSQL");
  694. $("#mysql_settings").toggle(dbType === "MySQL");
  695. $.each(dbDefaults, (_type, defaultHost) => {
  696. if ($("#db_host").val() === defaultHost) {
  697. $("#db_host").val(dbDefaults[dbType]);
  698. return false;
  699. }
  700. });
  701. });
  702. // TODO: better handling of exclusive relations.
  703. $("#offline-mode input").on("change", function () {
  704. if ($(this).is(":checked")) {
  705. $("#disable-gravatar").checkbox("check");
  706. $("#federated-avatar-lookup").checkbox("uncheck");
  707. }
  708. });
  709. $("#disable-gravatar input").on("change", function () {
  710. if ($(this).is(":checked")) {
  711. $("#federated-avatar-lookup").checkbox("uncheck");
  712. } else {
  713. $("#offline-mode").checkbox("uncheck");
  714. }
  715. });
  716. $("#federated-avatar-lookup input").on("change", function () {
  717. if ($(this).is(":checked")) {
  718. $("#disable-gravatar").checkbox("uncheck");
  719. $("#offline-mode").checkbox("uncheck");
  720. }
  721. });
  722. $("#enable-openid-signin input").on("change", function () {
  723. if ($(this).is(":checked")) {
  724. if (!$("#disable-registration input").is(":checked")) {
  725. $("#enable-openid-signup").checkbox("check");
  726. }
  727. } else {
  728. $("#enable-openid-signup").checkbox("uncheck");
  729. }
  730. });
  731. $("#disable-registration input").on("change", function () {
  732. if ($(this).is(":checked")) {
  733. $("#enable-captcha").checkbox("uncheck");
  734. $("#enable-openid-signup").checkbox("uncheck");
  735. } else {
  736. $("#enable-openid-signup").checkbox("check");
  737. }
  738. });
  739. $("#enable-captcha input").on("change", function () {
  740. if ($(this).is(":checked")) {
  741. $("#disable-registration").checkbox("uncheck");
  742. }
  743. });
  744. }
  745. function initIssueComments() {
  746. if ($(".repository.view.issue .timeline").length === 0) return;
  747. $(".re-request-review").on("click", function (event) {
  748. const url = $(this).data("update-url");
  749. const issueId = $(this).data("issue-id");
  750. const id = $(this).data("id");
  751. const isChecked = $(this).data("is-checked");
  752. //const ref = $(this).data('name');
  753. event.preventDefault();
  754. updateIssuesMeta(url, "", issueId, id, isChecked).then(reload);
  755. });
  756. $(document).on("click", (event) => {
  757. const urlTarget = $(":target");
  758. if (urlTarget.length === 0) return;
  759. const urlTargetId = urlTarget.attr("id");
  760. if (!urlTargetId) return;
  761. if (!/^(issue|pull)(comment)?-\d+$/.test(urlTargetId)) return;
  762. const $target = $(event.target);
  763. if ($target.closest(`#${urlTargetId}`).length === 0) {
  764. const scrollPosition = $(window).scrollTop();
  765. window.location.hash = "";
  766. $(window).scrollTop(scrollPosition);
  767. window.history.pushState(null, null, " ");
  768. }
  769. });
  770. }
  771. async function initRepository() {
  772. if ($(".repository").length === 0) {
  773. return;
  774. }
  775. function initFilterSearchDropdown(selector) {
  776. const $dropdown = $(selector);
  777. $dropdown.dropdown({
  778. fullTextSearch: true,
  779. selectOnKeydown: false,
  780. onChange(_text, _value, $choice) {
  781. if ($choice.data("url")) {
  782. window.location.href = $choice.data("url");
  783. }
  784. },
  785. message: { noResults: $dropdown.data("no-results") },
  786. });
  787. }
  788. // File list and commits
  789. if (
  790. $(".repository.file.list").length > 0 ||
  791. ".repository.commits".length > 0
  792. ) {
  793. initFilterBranchTagDropdown(".choose.reference .dropdown");
  794. }
  795. // Wiki
  796. if ($(".repository.wiki.view").length > 0) {
  797. initFilterSearchDropdown(".choose.page .dropdown");
  798. }
  799. // Options
  800. if ($(".repository.settings.options").length > 0) {
  801. // Enable or select internal/external wiki system and issue tracker.
  802. $(".enable-system").on("change", function () {
  803. if (this.checked) {
  804. $($(this).data("target")).removeClass("disabled");
  805. if (!$(this).data("context")) {
  806. $($(this).data("context")).addClass("disabled");
  807. }
  808. } else {
  809. $($(this).data("target")).addClass("disabled");
  810. if (!$(this).data("context")) {
  811. $($(this).data("context")).removeClass("disabled");
  812. }
  813. }
  814. });
  815. $(".enable-system-radio").on("change", function () {
  816. if (this.value === "false") {
  817. $($(this).data("target")).addClass("disabled");
  818. if (typeof $(this).data("context") !== "undefined") {
  819. $($(this).data("context")).removeClass("disabled");
  820. }
  821. } else if (this.value === "true") {
  822. $($(this).data("target")).removeClass("disabled");
  823. if (typeof $(this).data("context") !== "undefined") {
  824. $($(this).data("context")).addClass("disabled");
  825. }
  826. }
  827. });
  828. }
  829. // Labels
  830. if ($(".repository.labels").length > 0) {
  831. initLabelEdit();
  832. }
  833. // Milestones
  834. if ($(".repository.new.milestone").length > 0) {
  835. const $datepicker = $(".milestone.datepicker");
  836. await initDateTimePicker($datepicker.data("lang"));
  837. $datepicker.datetimepicker({
  838. inline: true,
  839. timepicker: false,
  840. startDate: $datepicker.data("start-date"),
  841. onSelectDate(date) {
  842. $("#deadline").val(date.toISOString().substring(0, 10));
  843. },
  844. });
  845. $("#clear-date").on("click", () => {
  846. $("#deadline").val("");
  847. return false;
  848. });
  849. }
  850. // Issues
  851. if ($(".repository.view.issue").length > 0) {
  852. // Edit issue title
  853. const $issueTitle = $("#issue-title");
  854. const $editInput = $("#edit-title-input input");
  855. const editTitleToggle = function () {
  856. $issueTitle.toggle();
  857. $(".not-in-edit").toggle();
  858. $("#edit-title-input").toggle();
  859. $("#pull-desc").toggle();
  860. $("#pull-desc-edit").toggle();
  861. $(".in-edit").toggle();
  862. $editInput.focus();
  863. return false;
  864. };
  865. const changeBranchSelect = function () {
  866. const selectionTextField = $("#pull-target-branch");
  867. const baseName = selectionTextField.data("basename");
  868. const branchNameNew = $(this).data("branch");
  869. const branchNameOld = selectionTextField.data("branch");
  870. // Replace branch name to keep translation from HTML template
  871. selectionTextField.html(
  872. selectionTextField
  873. .html()
  874. .replace(
  875. `${baseName}:${branchNameOld}`,
  876. `${baseName}:${branchNameNew}`
  877. )
  878. );
  879. selectionTextField.data("branch", branchNameNew); // update branch name in setting
  880. };
  881. $("#branch-select > .item").on("click", changeBranchSelect);
  882. $("#edit-title").on("click", editTitleToggle);
  883. $("#cancel-edit-title").on("click", editTitleToggle);
  884. $("#save-edit-title")
  885. .on("click", editTitleToggle)
  886. .on("click", function () {
  887. const pullrequest_targetbranch_change = function (update_url) {
  888. const targetBranch = $("#pull-target-branch").data("branch");
  889. const $branchTarget = $("#branch_target");
  890. if (targetBranch === $branchTarget.text()) {
  891. return false;
  892. }
  893. $.post(update_url, {
  894. _csrf: csrf,
  895. target_branch: targetBranch,
  896. })
  897. .done((data) => {
  898. $branchTarget.text(data.base_branch);
  899. })
  900. .always(() => {
  901. reload();
  902. });
  903. };
  904. const pullrequest_target_update_url = $(this).data("target-update-url");
  905. if (
  906. $editInput.val().length === 0 ||
  907. $editInput.val() === $issueTitle.text()
  908. ) {
  909. $editInput.val($issueTitle.text());
  910. pullrequest_targetbranch_change(pullrequest_target_update_url);
  911. } else {
  912. $.post(
  913. $(this).data("update-url"),
  914. {
  915. _csrf: csrf,
  916. title: $editInput.val(),
  917. },
  918. (data) => {
  919. $editInput.val(data.title);
  920. $issueTitle.text(data.title);
  921. pullrequest_targetbranch_change(pullrequest_target_update_url);
  922. reload();
  923. }
  924. );
  925. }
  926. return false;
  927. });
  928. // Issue Comments
  929. initIssueComments();
  930. // Issue/PR Context Menus
  931. $(".context-dropdown").dropdown({
  932. action: "hide",
  933. });
  934. // Quote reply
  935. $(".quote-reply").on("click", function (event) {
  936. $(this).closest(".dropdown").find(".menu").toggle("visible");
  937. const target = $(this).data("target");
  938. const quote = $(`#comment-${target}`).text().replace(/\n/g, "\n> ");
  939. const content = `> ${quote}\n\n`;
  940. let $content;
  941. if ($(this).hasClass("quote-reply-diff")) {
  942. const $parent = $(this).closest(".comment-code-cloud");
  943. $parent.find("button.comment-form-reply").trigger("click");
  944. $content = $parent.find('[name="content"]');
  945. if ($content.val() !== "") {
  946. $content.val(`${$content.val()}\n\n${content}`);
  947. } else {
  948. $content.val(`${content}`);
  949. }
  950. $content.focus();
  951. } else if (autoSimpleMDE !== null) {
  952. if (autoSimpleMDE.value() !== "") {
  953. autoSimpleMDE.value(`${autoSimpleMDE.value()}\n\n${content}`);
  954. } else {
  955. autoSimpleMDE.value(`${content}`);
  956. }
  957. }
  958. event.preventDefault();
  959. });
  960. // Edit issue or comment content
  961. $(".edit-content").on("click", async function (event) {
  962. $(this).closest(".dropdown").find(".menu").toggle("visible");
  963. const $segment = $(this).closest(".header").next();
  964. const $editContentZone = $segment.find(".edit-content-zone");
  965. const $renderContent = $segment.find(".render-content");
  966. const $rawContent = $segment.find(".raw-content");
  967. let $textarea;
  968. let $simplemde;
  969. // Setup new form
  970. if ($editContentZone.html().length === 0) {
  971. $editContentZone.html($("#edit-content-form").html());
  972. $textarea = $editContentZone.find("textarea");
  973. issuesTribute.attach($textarea.get());
  974. emojiTribute.attach($textarea.get());
  975. let dz;
  976. const $dropzone = $editContentZone.find(".dropzone");
  977. const $files = $editContentZone.find(".comment-files");
  978. if ($dropzone.length > 0) {
  979. $dropzone.data("saved", false);
  980. const filenameDict = {};
  981. dz = await createDropzone($dropzone[0], {
  982. url: $dropzone.data("upload-url"),
  983. headers: { "X-Csrf-Token": csrf },
  984. maxFiles: $dropzone.data("max-file"),
  985. maxFilesize: $dropzone.data("max-size"),
  986. acceptedFiles:
  987. $dropzone.data("accepts") === "*/*"
  988. ? null
  989. : $dropzone.data("accepts"),
  990. addRemoveLinks: true,
  991. dictDefaultMessage: $dropzone.data("default-message"),
  992. dictInvalidFileType: $dropzone.data("invalid-input-type"),
  993. dictFileTooBig: $dropzone.data("file-too-big"),
  994. dictRemoveFile: $dropzone.data("remove-file"),
  995. init() {
  996. this.on("success", (file, data) => {
  997. filenameDict[file.name] = {
  998. uuid: data.uuid,
  999. submitted: false,
  1000. };
  1001. const input = $(
  1002. `<input id="${data.uuid}" name="files" type="hidden">`
  1003. ).val(data.uuid);
  1004. $files.append(input);
  1005. });
  1006. this.on("removedfile", (file) => {
  1007. if (!(file.name in filenameDict)) {
  1008. return;
  1009. }
  1010. $(`#${filenameDict[file.name].uuid}`).remove();
  1011. if (
  1012. $dropzone.data("remove-url") &&
  1013. $dropzone.data("csrf") &&
  1014. !filenameDict[file.name].submitted
  1015. ) {
  1016. $.post($dropzone.data("remove-url"), {
  1017. file: filenameDict[file.name].uuid,
  1018. _csrf: $dropzone.data("csrf"),
  1019. });
  1020. }
  1021. });
  1022. this.on("submit", () => {
  1023. $.each(filenameDict, (name) => {
  1024. filenameDict[name].submitted = true;
  1025. });
  1026. });
  1027. this.on("reload", () => {
  1028. $.getJSON($editContentZone.data("attachment-url"), (data) => {
  1029. dz.removeAllFiles(true);
  1030. $files.empty();
  1031. $.each(data, function () {
  1032. const imgSrc = `${$dropzone.data("upload-url")}/${
  1033. this.uuid
  1034. }`;
  1035. dz.emit("addedfile", this);
  1036. dz.emit("thumbnail", this, imgSrc);
  1037. dz.emit("complete", this);
  1038. dz.files.push(this);
  1039. filenameDict[this.name] = {
  1040. submitted: true,
  1041. uuid: this.uuid,
  1042. };
  1043. $dropzone
  1044. .find(`img[src='${imgSrc}']`)
  1045. .css("max-width", "100%");
  1046. const input = $(
  1047. `<input id="${this.uuid}" name="files" type="hidden">`
  1048. ).val(this.uuid);
  1049. $files.append(input);
  1050. });
  1051. });
  1052. });
  1053. },
  1054. });
  1055. dz.emit("reload");
  1056. }
  1057. // Give new write/preview data-tab name to distinguish from others
  1058. const $editContentForm = $editContentZone.find(".ui.comment.form");
  1059. const $tabMenu = $editContentForm.find(".tabular.menu");
  1060. $tabMenu.attr("data-write", $editContentZone.data("write"));
  1061. $tabMenu.attr("data-preview", $editContentZone.data("preview"));
  1062. $tabMenu
  1063. .find(".write.item")
  1064. .attr("data-tab", $editContentZone.data("write"));
  1065. $tabMenu
  1066. .find(".preview.item")
  1067. .attr("data-tab", $editContentZone.data("preview"));
  1068. $editContentForm
  1069. .find(".write")
  1070. .attr("data-tab", $editContentZone.data("write"));
  1071. $editContentForm
  1072. .find(".preview")
  1073. .attr("data-tab", $editContentZone.data("preview"));
  1074. $simplemde = setCommentSimpleMDE($textarea);
  1075. commentMDEditors[$editContentZone.data("write")] = $simplemde;
  1076. initCommentPreviewTab($editContentForm);
  1077. initSimpleMDEImagePaste($simplemde, $files);
  1078. $editContentZone.find(".cancel.button").on("click", () => {
  1079. $renderContent.show();
  1080. $editContentZone.hide();
  1081. dz.emit("reload");
  1082. });
  1083. $editContentZone.find(".save.button").on("click", () => {
  1084. $renderContent.show();
  1085. $editContentZone.hide();
  1086. const $attachments = $files
  1087. .find("[name=files]")
  1088. .map(function () {
  1089. return $(this).val();
  1090. })
  1091. .get();
  1092. $.post(
  1093. $editContentZone.data("update-url"),
  1094. {
  1095. _csrf: csrf,
  1096. content: $textarea.val(),
  1097. context: $editContentZone.data("context"),
  1098. files: $attachments,
  1099. },
  1100. (data) => {
  1101. if (data.length === 0 || data.content === "") {
  1102. $renderContent.html($("#no-content").html());
  1103. } else {
  1104. $renderContent.html(data.content);
  1105. $("pre code", $renderContent[0]).each(function () {
  1106. highlight(this);
  1107. });
  1108. }
  1109. let imageShow = "";
  1110. const $content = $segment.parent();
  1111. if (!$content.find(".ui.small.images").length) {
  1112. if (data.attachments !== "" && data.attachments) {
  1113. if ($content.find(".ui.middle.aligned").length === 0) {
  1114. imageShow += '<div class="ui clearing divider"></div>';
  1115. imageShow += '<div class="ui middle aligned padded grid">';
  1116. imageShow += data.attachments;
  1117. imageShow += "</div>";
  1118. $content.find(".ui.attached.segment").append(imageShow);
  1119. } else {
  1120. $content.find(".ui.middle.aligned").html(data.attachments);
  1121. }
  1122. }
  1123. } else if (data.attachments === "") {
  1124. $content.find(".ui.small.images").parent().remove();
  1125. } else {
  1126. $content.find(".ui.small.images").html(data.attachments);
  1127. }
  1128. dz.emit("submit");
  1129. dz.emit("reload");
  1130. }
  1131. );
  1132. });
  1133. } else {
  1134. $textarea = $segment.find("textarea");
  1135. $simplemde = commentMDEditors[$editContentZone.data("write")];
  1136. }
  1137. // Show write/preview tab and copy raw content as needed
  1138. $editContentZone.show();
  1139. $renderContent.hide();
  1140. if ($textarea.val().length === 0) {
  1141. $textarea.val($rawContent.text());
  1142. $simplemde.value($rawContent.text());
  1143. }
  1144. $textarea.focus();
  1145. $simplemde.codemirror.focus();
  1146. event.preventDefault();
  1147. });
  1148. // Delete comment
  1149. $(".delete-comment").on("click", function () {
  1150. const $this = $(this);
  1151. if (window.confirm($this.data("locale"))) {
  1152. $.post($this.data("url"), {
  1153. _csrf: csrf,
  1154. }).done(() => {
  1155. $(`#${$this.data("comment-id")}`).remove();
  1156. });
  1157. }
  1158. return false;
  1159. });
  1160. // Change status
  1161. const $statusButton = $("#status-button");
  1162. $("#comment-form .edit_area").on("keyup", function () {
  1163. if ($(this).val().length === 0) {
  1164. $statusButton.text($statusButton.data("status"));
  1165. } else {
  1166. $statusButton.text($statusButton.data("status-and-comment"));
  1167. }
  1168. });
  1169. $statusButton.on("click", () => {
  1170. $("#status").val($statusButton.data("status-val"));
  1171. $("#comment-form").trigger("submit");
  1172. });
  1173. // Pull Request merge button
  1174. const $mergeButton = $(".merge-button > button");
  1175. $mergeButton.on("click", function (e) {
  1176. e.preventDefault();
  1177. $(`.${$(this).data("do")}-fields`).show();
  1178. $(this).parent().hide();
  1179. });
  1180. $(".merge-button > .dropdown").dropdown({
  1181. onChange(_text, _value, $choice) {
  1182. if ($choice.data("do")) {
  1183. $mergeButton.find(".button-text").text($choice.text());
  1184. $mergeButton.data("do", $choice.data("do"));
  1185. }
  1186. },
  1187. });
  1188. $(".merge-cancel").on("click", function (e) {
  1189. e.preventDefault();
  1190. $(this).closest(".form").hide();
  1191. $mergeButton.parent().show();
  1192. });
  1193. initReactionSelector();
  1194. }
  1195. // Datasets
  1196. if ($(".repository.dataset-list.view").length > 0) {
  1197. const editContentToggle = function () {
  1198. $("#dataset-content").toggle();
  1199. $("#dataset-content-edit").toggle();
  1200. $("#dataset-content input").focus();
  1201. return false;
  1202. };
  1203. $("[data-dataset-status]").on("click", function () {
  1204. const $this = $(this);
  1205. const $private = $this.data("private");
  1206. const $is_private = $this.data("is-private");
  1207. if ($is_private === $private) {
  1208. return;
  1209. }
  1210. const $uuid = $this.data("uuid");
  1211. $.post($this.data("url"), {
  1212. _csrf: $this.data("csrf"),
  1213. file: $uuid,
  1214. is_private: $private,
  1215. })
  1216. .done((_data) => {
  1217. $(`[data-uuid='${$uuid}']`).removeClass("positive active");
  1218. $(`[data-uuid='${$uuid}']`).data("is-private", $private);
  1219. $this.addClass("positive active");
  1220. })
  1221. .fail(() => {
  1222. window.location.reload();
  1223. });
  1224. });
  1225. $("[data-dataset-delete]").on("click", function () {
  1226. const $this = $(this);
  1227. $("#data-dataset-delete-modal")
  1228. .modal({
  1229. closable: false,
  1230. onApprove() {
  1231. $.post($this.data("remove-url"), {
  1232. _csrf: $this.data("csrf"),
  1233. file: $this.data("uuid"),
  1234. })
  1235. .done((_data) => {
  1236. $(`#${$this.data("uuid")}`).hide();
  1237. })
  1238. .fail(() => {
  1239. window.location.reload();
  1240. });
  1241. },
  1242. })
  1243. .modal("show");
  1244. });
  1245. $("[data-category-id]").on("click", function () {
  1246. const category = $(this).data("category-id");
  1247. $("#category").val(category);
  1248. $("#submit").click();
  1249. });
  1250. $("[data-task-id]").on("click", function () {
  1251. const task = $(this).data("task-id");
  1252. $("#task").val(task);
  1253. $("#submit").click();
  1254. });
  1255. $("[data-license-id]").on("click", function () {
  1256. const license = $(this).data("license-id");
  1257. $("#license").val(license);
  1258. $("#submit").click();
  1259. });
  1260. $("#dataset-edit").on("click", editContentToggle);
  1261. $("#cancel").on("click", editContentToggle);
  1262. }
  1263. // Diff
  1264. if ($(".repository.diff").length > 0) {
  1265. $(".diff-counter").each(function () {
  1266. const $item = $(this);
  1267. const addLine = $item.find("span[data-line].add").data("line");
  1268. const delLine = $item.find("span[data-line].del").data("line");
  1269. const addPercent =
  1270. (parseFloat(addLine) / (parseFloat(addLine) + parseFloat(delLine))) *
  1271. 100;
  1272. $item.find(".bar .add").css("width", `${addPercent}%`);
  1273. });
  1274. }
  1275. // Quick start and repository home
  1276. $("#repo-clone-ssh").on("click", function () {
  1277. $(".clone-url").text($(this).data("link"));
  1278. $("#repo-clone-url").val($(this).data("link"));
  1279. $(this).addClass("blue");
  1280. $("#repo-clone-https").removeClass("blue");
  1281. localStorage.setItem("repo-clone-protocol", "ssh");
  1282. });
  1283. $("#repo-clone-https").on("click", function () {
  1284. $(".clone-url").text($(this).data("link"));
  1285. $("#repo-clone-url").val($(this).data("link"));
  1286. $(this).addClass("blue");
  1287. $("#repo-clone-ssh").removeClass("blue");
  1288. localStorage.setItem("repo-clone-protocol", "https");
  1289. });
  1290. $("#repo-clone-url").on("click", function () {
  1291. $(this).select();
  1292. });
  1293. // Pull request
  1294. const $repoComparePull = $(".repository.compare.pull");
  1295. if ($repoComparePull.length > 0) {
  1296. initFilterSearchDropdown(".choose.branch .dropdown");
  1297. // show pull request form
  1298. $repoComparePull.find("button.show-form").on("click", function (e) {
  1299. e.preventDefault();
  1300. $repoComparePull.find(".pullrequest-form").show();
  1301. autoSimpleMDE.codemirror.refresh();
  1302. $(this).parent().hide();
  1303. });
  1304. }
  1305. // Branches
  1306. if ($(".repository.settings.branches").length > 0) {
  1307. initFilterSearchDropdown(".protected-branches .dropdown");
  1308. $(".enable-protection, .enable-whitelist, .enable-statuscheck").on(
  1309. "change",
  1310. function () {
  1311. if (this.checked) {
  1312. $($(this).data("target")).removeClass("disabled");
  1313. } else {
  1314. $($(this).data("target")).addClass("disabled");
  1315. }
  1316. }
  1317. );
  1318. $(".disable-whitelist").on("change", function () {
  1319. if (this.checked) {
  1320. $($(this).data("target")).addClass("disabled");
  1321. }
  1322. });
  1323. }
  1324. // Language stats
  1325. if ($(".language-stats").length > 0) {
  1326. $(".language-stats").on("click", (e) => {
  1327. e.preventDefault();
  1328. $(".language-stats-details, .repository-menu").slideToggle();
  1329. });
  1330. }
  1331. }
  1332. function initMigration() {
  1333. const toggleMigrations = function () {
  1334. const authUserName = $("#auth_username").val();
  1335. const cloneAddr = $("#clone_addr").val();
  1336. if (
  1337. !$("#mirror").is(":checked") &&
  1338. authUserName &&
  1339. authUserName.length > 0 &&
  1340. cloneAddr !== undefined &&
  1341. (cloneAddr.startsWith("https://github.com") ||
  1342. cloneAddr.startsWith("http://github.com") ||
  1343. cloneAddr.startsWith("http://gitlab.com") ||
  1344. cloneAddr.startsWith("https://gitlab.com"))
  1345. ) {
  1346. $("#migrate_items").show();
  1347. } else {
  1348. $("#migrate_items").hide();
  1349. }
  1350. };
  1351. toggleMigrations();
  1352. $("#clone_addr").on("input", toggleMigrations);
  1353. $("#auth_username").on("input", toggleMigrations);
  1354. $("#mirror").on("change", toggleMigrations);
  1355. }
  1356. function initPullRequestReview() {
  1357. $(".show-outdated").on("click", function (e) {
  1358. e.preventDefault();
  1359. const id = $(this).data("comment");
  1360. $(this).addClass("hide");
  1361. $(`#code-comments-${id}`).removeClass("hide");
  1362. $(`#code-preview-${id}`).removeClass("hide");
  1363. $(`#hide-outdated-${id}`).removeClass("hide");
  1364. });
  1365. $(".hide-outdated").on("click", function (e) {
  1366. e.preventDefault();
  1367. const id = $(this).data("comment");
  1368. $(this).addClass("hide");
  1369. $(`#code-comments-${id}`).addClass("hide");
  1370. $(`#code-preview-${id}`).addClass("hide");
  1371. $(`#show-outdated-${id}`).removeClass("hide");
  1372. });
  1373. $("button.comment-form-reply").on("click", function (e) {
  1374. e.preventDefault();
  1375. $(this).hide();
  1376. const form = $(this).parent().find(".comment-form");
  1377. form.removeClass("hide");
  1378. assingMenuAttributes(form.find(".menu"));
  1379. });
  1380. // The following part is only for diff views
  1381. if ($(".repository.pull.diff").length === 0) {
  1382. return;
  1383. }
  1384. $(".diff-detail-box.ui.sticky").sticky();
  1385. $(".btn-review")
  1386. .on("click", function (e) {
  1387. e.preventDefault();
  1388. $(this).closest(".dropdown").find(".menu").toggle("visible");
  1389. })
  1390. .closest(".dropdown")
  1391. .find(".link.close")
  1392. .on("click", function (e) {
  1393. e.preventDefault();
  1394. $(this).closest(".menu").toggle("visible");
  1395. });
  1396. $(".code-view .lines-code,.code-view .lines-num")
  1397. .on("mouseenter", function () {
  1398. const parent = $(this).closest("td");
  1399. $(this)
  1400. .closest("tr")
  1401. .addClass(
  1402. parent.hasClass("lines-num-old") || parent.hasClass("lines-code-old")
  1403. ? "focus-lines-old"
  1404. : "focus-lines-new"
  1405. );
  1406. })
  1407. .on("mouseleave", function () {
  1408. $(this).closest("tr").removeClass("focus-lines-new focus-lines-old");
  1409. });
  1410. $(".add-code-comment").on("click", function (e) {
  1411. // https://github.com/go-gitea/gitea/issues/4745
  1412. if ($(e.target).hasClass("btn-add-single")) {
  1413. return;
  1414. }
  1415. e.preventDefault();
  1416. const isSplit = $(this).closest(".code-diff").hasClass("code-diff-split");
  1417. const side = $(this).data("side");
  1418. const idx = $(this).data("idx");
  1419. const path = $(this).data("path");
  1420. const form = $("#pull_review_add_comment").html();
  1421. const tr = $(this).closest("tr");
  1422. let ntr = tr.next();
  1423. if (!ntr.hasClass("add-comment")) {
  1424. ntr = $(
  1425. `<tr class="add-comment">${
  1426. isSplit
  1427. ? '<td class="lines-num"></td><td class="lines-type-marker"></td><td class="add-comment-left"></td><td class="lines-num"></td><td class="lines-type-marker"></td><td class="add-comment-right"></td>'
  1428. : '<td class="lines-num"></td><td class="lines-num"></td><td class="add-comment-left add-comment-right" colspan="2"></td>'
  1429. }</tr>`
  1430. );
  1431. tr.after(ntr);
  1432. }
  1433. const td = ntr.find(`.add-comment-${side}`);
  1434. let commentCloud = td.find(".comment-code-cloud");
  1435. if (commentCloud.length === 0) {
  1436. td.html(form);
  1437. commentCloud = td.find(".comment-code-cloud");
  1438. assingMenuAttributes(commentCloud.find(".menu"));
  1439. td.find("input[name='line']").val(idx);
  1440. td.find("input[name='side']").val(
  1441. side === "left" ? "previous" : "proposed"
  1442. );
  1443. td.find("input[name='path']").val(path);
  1444. }
  1445. commentCloud.find("textarea").focus();
  1446. });
  1447. }
  1448. function assingMenuAttributes(menu) {
  1449. const id = Math.floor(Math.random() * Math.floor(1000000));
  1450. menu.attr("data-write", menu.attr("data-write") + id);
  1451. menu.attr("data-preview", menu.attr("data-preview") + id);
  1452. menu.find(".item").each(function () {
  1453. const tab = $(this).attr("data-tab") + id;
  1454. $(this).attr("data-tab", tab);
  1455. });
  1456. menu.parent().find("*[data-tab='write']").attr("data-tab", `write${id}`);
  1457. menu.parent().find("*[data-tab='preview']").attr("data-tab", `preview${id}`);
  1458. initCommentPreviewTab(menu.parent(".form"));
  1459. return id;
  1460. }
  1461. function initRepositoryCollaboration() {
  1462. // Change collaborator access mode
  1463. $(".access-mode.menu .item").on("click", function () {
  1464. const $menu = $(this).parent();
  1465. $.post($menu.data("url"), {
  1466. _csrf: csrf,
  1467. uid: $menu.data("uid"),
  1468. mode: $(this).data("value"),
  1469. });
  1470. });
  1471. }
  1472. function initTeamSettings() {
  1473. // Change team access mode
  1474. $(".organization.new.team input[name=permission]").on("change", () => {
  1475. const val = $(
  1476. "input[name=permission]:checked",
  1477. ".organization.new.team"
  1478. ).val();
  1479. if (val === "admin") {
  1480. $(".organization.new.team .team-units").hide();
  1481. } else {
  1482. $(".organization.new.team .team-units").show();
  1483. }
  1484. });
  1485. }
  1486. function initWikiForm() {
  1487. const $editArea = $(".repository.wiki textarea#edit_area");
  1488. let sideBySideChanges = 0;
  1489. let sideBySideTimeout = null;
  1490. if ($editArea.length > 0) {
  1491. const simplemde = new SimpleMDE({
  1492. autoDownloadFontAwesome: false,
  1493. element: $editArea[0],
  1494. forceSync: true,
  1495. previewRender(plainText, preview) {
  1496. // Async method
  1497. setTimeout(() => {
  1498. // FIXME: still send render request when return back to edit mode
  1499. const render = function () {
  1500. sideBySideChanges = 0;
  1501. if (sideBySideTimeout !== null) {
  1502. clearTimeout(sideBySideTimeout);
  1503. sideBySideTimeout = null;
  1504. }
  1505. $.post(
  1506. $editArea.data("url"),
  1507. {
  1508. _csrf: csrf,
  1509. mode: "gfm",
  1510. context: $editArea.data("context"),
  1511. text: plainText,
  1512. },
  1513. (data) => {
  1514. preview.innerHTML = `<div class="markdown ui segment">${data}</div>`;
  1515. $(preview)
  1516. .find("pre code")
  1517. .each((_, e) => {
  1518. highlight(e);
  1519. });
  1520. }
  1521. );
  1522. };
  1523. if (!simplemde.isSideBySideActive()) {
  1524. render();
  1525. } else {
  1526. // delay preview by keystroke counting
  1527. sideBySideChanges++;
  1528. if (sideBySideChanges > 10) {
  1529. render();
  1530. }
  1531. // or delay preview by timeout
  1532. if (sideBySideTimeout !== null) {
  1533. clearTimeout(sideBySideTimeout);
  1534. sideBySideTimeout = null;
  1535. }
  1536. sideBySideTimeout = setTimeout(render, 600);
  1537. }
  1538. }, 0);
  1539. if (!simplemde.isSideBySideActive()) {
  1540. return "Loading...";
  1541. }
  1542. return preview.innerHTML;
  1543. },
  1544. renderingConfig: {
  1545. singleLineBreaks: false,
  1546. },
  1547. indentWithTabs: false,
  1548. tabSize: 4,
  1549. spellChecker: false,
  1550. toolbar: [
  1551. "bold",
  1552. "italic",
  1553. "strikethrough",
  1554. "|",
  1555. "heading-1",
  1556. "heading-2",
  1557. "heading-3",
  1558. "heading-bigger",
  1559. "heading-smaller",
  1560. "|",
  1561. {
  1562. name: "code-inline",
  1563. action(e) {
  1564. const cm = e.codemirror;
  1565. const selection = cm.getSelection();
  1566. cm.replaceSelection(`\`${selection}\``);
  1567. if (!selection) {
  1568. const cursorPos = cm.getCursor();
  1569. cm.setCursor(cursorPos.line, cursorPos.ch - 1);
  1570. }
  1571. cm.focus();
  1572. },
  1573. className: "fa fa-angle-right",
  1574. title: "Add Inline Code",
  1575. },
  1576. "code",
  1577. "quote",
  1578. "|",
  1579. {
  1580. name: "checkbox-empty",
  1581. action(e) {
  1582. const cm = e.codemirror;
  1583. cm.replaceSelection(`\n- [ ] ${cm.getSelection()}`);
  1584. cm.focus();
  1585. },
  1586. className: "fa fa-square-o",
  1587. title: "Add Checkbox (empty)",
  1588. },
  1589. {
  1590. name: "checkbox-checked",
  1591. action(e) {
  1592. const cm = e.codemirror;
  1593. cm.replaceSelection(`\n- [x] ${cm.getSelection()}`);
  1594. cm.focus();
  1595. },
  1596. className: "fa fa-check-square-o",
  1597. title: "Add Checkbox (checked)",
  1598. },
  1599. "|",
  1600. "unordered-list",
  1601. "ordered-list",
  1602. "|",
  1603. "link",
  1604. "image",
  1605. "table",
  1606. "horizontal-rule",
  1607. "|",
  1608. "clean-block",
  1609. "preview",
  1610. "fullscreen",
  1611. "side-by-side",
  1612. "|",
  1613. {
  1614. name: "revert-to-textarea",
  1615. action(e) {
  1616. e.toTextArea();
  1617. },
  1618. className: "fa fa-file",
  1619. title: "Revert to simple textarea",
  1620. },
  1621. ],
  1622. });
  1623. $(simplemde.codemirror.getInputField()).addClass("js-quick-submit");
  1624. setTimeout(() => {
  1625. const $bEdit = $('.repository.wiki.new .previewtabs a[data-tab="write"]');
  1626. const $bPrev = $(
  1627. '.repository.wiki.new .previewtabs a[data-tab="preview"]'
  1628. );
  1629. const $toolbar = $(".editor-toolbar");
  1630. const $bPreview = $(".editor-toolbar a.fa-eye");
  1631. const $bSideBySide = $(".editor-toolbar a.fa-columns");
  1632. $bEdit.on("click", () => {
  1633. if ($toolbar.hasClass("disabled-for-preview")) {
  1634. $bPreview.trigger("click");
  1635. }
  1636. });
  1637. $bPrev.on("click", () => {
  1638. if (!$toolbar.hasClass("disabled-for-preview")) {
  1639. $bPreview.trigger("click");
  1640. }
  1641. });
  1642. $bPreview.on("click", () => {
  1643. setTimeout(() => {
  1644. if ($toolbar.hasClass("disabled-for-preview")) {
  1645. if ($bEdit.hasClass("active")) {
  1646. $bEdit.removeClass("active");
  1647. }
  1648. if (!$bPrev.hasClass("active")) {
  1649. $bPrev.addClass("active");
  1650. }
  1651. } else {
  1652. if (!$bEdit.hasClass("active")) {
  1653. $bEdit.addClass("active");
  1654. }
  1655. if ($bPrev.hasClass("active")) {
  1656. $bPrev.removeClass("active");
  1657. }
  1658. }
  1659. }, 0);
  1660. });
  1661. $bSideBySide.on("click", () => {
  1662. sideBySideChanges = 10;
  1663. });
  1664. }, 0);
  1665. }
  1666. }
  1667. // Adding function to get the cursor position in a text field to jQuery object.
  1668. $.fn.getCursorPosition = function () {
  1669. const el = $(this).get(0);
  1670. let pos = 0;
  1671. if ("selectionStart" in el) {
  1672. pos = el.selectionStart;
  1673. } else if ("selection" in document) {
  1674. el.focus();
  1675. const Sel = document.selection.createRange();
  1676. const SelLength = document.selection.createRange().text.length;
  1677. Sel.moveStart("character", -el.value.length);
  1678. pos = Sel.text.length - SelLength;
  1679. }
  1680. return pos;
  1681. };
  1682. function setCommentSimpleMDE($editArea) {
  1683. const simplemde = new SimpleMDE({
  1684. autoDownloadFontAwesome: false,
  1685. element: $editArea[0],
  1686. forceSync: true,
  1687. renderingConfig: {
  1688. singleLineBreaks: false,
  1689. },
  1690. indentWithTabs: false,
  1691. tabSize: 4,
  1692. spellChecker: false,
  1693. toolbar: [
  1694. "bold",
  1695. "italic",
  1696. "strikethrough",
  1697. "|",
  1698. "heading-1",
  1699. "heading-2",
  1700. "heading-3",
  1701. "heading-bigger",
  1702. "heading-smaller",
  1703. "|",
  1704. "code",
  1705. "quote",
  1706. "|",
  1707. "unordered-list",
  1708. "ordered-list",
  1709. "|",
  1710. "link",
  1711. "image",
  1712. "table",
  1713. "horizontal-rule",
  1714. "|",
  1715. "clean-block",
  1716. "|",
  1717. {
  1718. name: "revert-to-textarea",
  1719. action(e) {
  1720. e.toTextArea();
  1721. },
  1722. className: "fa fa-file",
  1723. title: "Revert to simple textarea",
  1724. },
  1725. ],
  1726. });
  1727. $(simplemde.codemirror.getInputField()).addClass("js-quick-submit");
  1728. simplemde.codemirror.setOption("extraKeys", {
  1729. Enter: () => {
  1730. if (!(issuesTribute.isActive || emojiTribute.isActive)) {
  1731. return CodeMirror.Pass;
  1732. }
  1733. },
  1734. Backspace: (cm) => {
  1735. if (cm.getInputField().trigger) {
  1736. cm.getInputField().trigger("input");
  1737. }
  1738. cm.execCommand("delCharBefore");
  1739. },
  1740. });
  1741. issuesTribute.attach(simplemde.codemirror.getInputField());
  1742. emojiTribute.attach(simplemde.codemirror.getInputField());
  1743. return simplemde;
  1744. }
  1745. async function initEditor() {
  1746. $(".js-quick-pull-choice-option").on("change", function () {
  1747. if ($(this).val() === "commit-to-new-branch") {
  1748. $(".quick-pull-branch-name").show();
  1749. $(".quick-pull-branch-name input").prop("required", true);
  1750. } else {
  1751. $(".quick-pull-branch-name").hide();
  1752. $(".quick-pull-branch-name input").prop("required", false);
  1753. }
  1754. $("#commit-button").text($(this).attr("button_text"));
  1755. });
  1756. const $editFilename = $("#file-name");
  1757. $editFilename
  1758. .on("keyup", function (e) {
  1759. const $section = $(".breadcrumb span.section");
  1760. const $divider = $(".breadcrumb div.divider");
  1761. let value;
  1762. let parts;
  1763. if (e.keyCode === 8) {
  1764. if ($(this).getCursorPosition() === 0) {
  1765. if ($section.length > 0) {
  1766. value = $section.last().find("a").text();
  1767. $(this).val(value + $(this).val());
  1768. $(this)[0].setSelectionRange(value.length, value.length);
  1769. $section.last().remove();
  1770. $divider.last().remove();
  1771. }
  1772. }
  1773. }
  1774. if (e.keyCode === 191) {
  1775. parts = $(this).val().split("/");
  1776. for (let i = 0; i < parts.length; ++i) {
  1777. value = parts[i];
  1778. if (i < parts.length - 1) {
  1779. if (value.length) {
  1780. $(
  1781. `<span class="section"><a href="#">${value}</a></span>`
  1782. ).insertBefore($(this));
  1783. $('<div class="divider"> / </div>').insertBefore($(this));
  1784. }
  1785. } else {
  1786. $(this).val(value);
  1787. }
  1788. $(this)[0].setSelectionRange(0, 0);
  1789. }
  1790. }
  1791. parts = [];
  1792. $(".breadcrumb span.section").each(function () {
  1793. const element = $(this);
  1794. if (element.find("a").length) {
  1795. parts.push(element.find("a").text());
  1796. } else {
  1797. parts.push(element.text());
  1798. }
  1799. });
  1800. if ($(this).val()) parts.push($(this).val());
  1801. $("#tree_path").val(parts.join("/"));
  1802. })
  1803. .trigger("keyup");
  1804. const $editArea = $(".repository.editor textarea#edit_area");
  1805. if (!$editArea.length) return;
  1806. await createCodeEditor($editArea[0], $editFilename[0], previewFileModes);
  1807. // Using events from https://github.com/codedance/jquery.AreYouSure#advanced-usage
  1808. // to enable or disable the commit button
  1809. const $commitButton = $("#commit-button");
  1810. const $editForm = $(".ui.edit.form");
  1811. const dirtyFileClass = "dirty-file";
  1812. // Disabling the button at the start
  1813. $commitButton.prop("disabled", true);
  1814. // Registering a custom listener for the file path and the file content
  1815. $editForm.areYouSure({
  1816. silent: true,
  1817. dirtyClass: dirtyFileClass,
  1818. fieldSelector: ":input:not(.commit-form-wrapper :input)",
  1819. change() {
  1820. const dirty = $(this).hasClass(dirtyFileClass);
  1821. $commitButton.prop("disabled", !dirty);
  1822. },
  1823. });
  1824. $commitButton.on("click", (event) => {
  1825. // A modal which asks if an empty file should be committed
  1826. if ($editArea.val().length === 0) {
  1827. $("#edit-empty-content-modal")
  1828. .modal({
  1829. onApprove() {
  1830. $(".edit.form").trigger("submit");
  1831. },
  1832. })
  1833. .modal("show");
  1834. event.preventDefault();
  1835. }
  1836. });
  1837. }
  1838. function initOrganization() {
  1839. if ($(".organization").length === 0) {
  1840. return;
  1841. }
  1842. // Options
  1843. if ($(".organization.settings.options").length > 0) {
  1844. $("#org_name").on("keyup", function () {
  1845. const $prompt = $("#org-name-change-prompt");
  1846. if (
  1847. $(this).val().toString().toLowerCase() !==
  1848. $(this).data("org-name").toString().toLowerCase()
  1849. ) {
  1850. $prompt.show();
  1851. } else {
  1852. $prompt.hide();
  1853. }
  1854. });
  1855. }
  1856. // Labels
  1857. if ($(".organization.settings.labels").length > 0) {
  1858. initLabelEdit();
  1859. }
  1860. }
  1861. function initUserSettings() {
  1862. // Options
  1863. if ($(".user.settings.profile").length > 0) {
  1864. $("#username").on("keyup", function () {
  1865. const $prompt = $("#name-change-prompt");
  1866. if (
  1867. $(this).val().toString().toLowerCase() !==
  1868. $(this).data("name").toString().toLowerCase()
  1869. ) {
  1870. $prompt.show();
  1871. } else {
  1872. $prompt.hide();
  1873. }
  1874. });
  1875. }
  1876. }
  1877. function initGithook() {
  1878. if ($(".edit.githook").length === 0) {
  1879. return;
  1880. }
  1881. CodeMirror.autoLoadMode(
  1882. CodeMirror.fromTextArea($("#content")[0], {
  1883. lineNumbers: true,
  1884. mode: "shell",
  1885. }),
  1886. "shell"
  1887. );
  1888. }
  1889. function initWebhook() {
  1890. if ($(".new.webhook").length === 0) {
  1891. return;
  1892. }
  1893. $(".events.checkbox input").on("change", function () {
  1894. if ($(this).is(":checked")) {
  1895. $(".events.fields").show();
  1896. }
  1897. });
  1898. $(".non-events.checkbox input").on("change", function () {
  1899. if ($(this).is(":checked")) {
  1900. $(".events.fields").hide();
  1901. }
  1902. });
  1903. const updateContentType = function () {
  1904. const visible = $("#http_method").val() === "POST";
  1905. $("#content_type").parent().parent()[visible ? "show" : "hide"]();
  1906. };
  1907. updateContentType();
  1908. $("#http_method").on("change", () => {
  1909. updateContentType();
  1910. });
  1911. // Test delivery
  1912. $("#test-delivery").on("click", function () {
  1913. const $this = $(this);
  1914. $this.addClass("loading disabled");
  1915. $.post($this.data("link"), {
  1916. _csrf: csrf,
  1917. }).done(
  1918. setTimeout(() => {
  1919. window.location.href = $this.data("redirect");
  1920. }, 5000)
  1921. );
  1922. });
  1923. }
  1924. function initAdmin() {
  1925. if ($(".admin").length === 0) {
  1926. return;
  1927. }
  1928. // New user
  1929. if ($(".admin.new.user").length > 0 || $(".admin.edit.user").length > 0) {
  1930. $("#login_type").on("change", function () {
  1931. if ($(this).val().substring(0, 1) === "0") {
  1932. $("#login_name").removeAttr("required");
  1933. $(".non-local").hide();
  1934. $(".local").show();
  1935. $("#user_name").focus();
  1936. if ($(this).data("password") === "required") {
  1937. $("#password").attr("required", "required");
  1938. }
  1939. } else {
  1940. $("#login_name").attr("required", "required");
  1941. $(".non-local").show();
  1942. $(".local").hide();
  1943. $("#login_name").focus();
  1944. $("#password").removeAttr("required");
  1945. }
  1946. });
  1947. }
  1948. function onSecurityProtocolChange() {
  1949. if ($("#security_protocol").val() > 0) {
  1950. $(".has-tls").show();
  1951. } else {
  1952. $(".has-tls").hide();
  1953. }
  1954. }
  1955. function onUsePagedSearchChange() {
  1956. if ($("#use_paged_search").prop("checked")) {
  1957. $(".search-page-size").show().find("input").attr("required", "required");
  1958. } else {
  1959. $(".search-page-size").hide().find("input").removeAttr("required");
  1960. }
  1961. }
  1962. function onOAuth2Change() {
  1963. $(".open_id_connect_auto_discovery_url, .oauth2_use_custom_url").hide();
  1964. $(".open_id_connect_auto_discovery_url input[required]").removeAttr(
  1965. "required"
  1966. );
  1967. const provider = $("#oauth2_provider").val();
  1968. switch (provider) {
  1969. case "github":
  1970. case "gitlab":
  1971. case "gitea":
  1972. case "nextcloud":
  1973. $(".oauth2_use_custom_url").show();
  1974. break;
  1975. case "openidConnect":
  1976. $(".open_id_connect_auto_discovery_url input").attr(
  1977. "required",
  1978. "required"
  1979. );
  1980. $(".open_id_connect_auto_discovery_url").show();
  1981. break;
  1982. }
  1983. onOAuth2UseCustomURLChange();
  1984. }
  1985. function onOAuth2UseCustomURLChange() {
  1986. const provider = $("#oauth2_provider").val();
  1987. $(".oauth2_use_custom_url_field").hide();
  1988. $(".oauth2_use_custom_url_field input[required]").removeAttr("required");
  1989. if ($("#oauth2_use_custom_url").is(":checked")) {
  1990. $("#oauth2_token_url").val($(`#${provider}_token_url`).val());
  1991. $("#oauth2_auth_url").val($(`#${provider}_auth_url`).val());
  1992. $("#oauth2_profile_url").val($(`#${provider}_profile_url`).val());
  1993. $("#oauth2_email_url").val($(`#${provider}_email_url`).val());
  1994. switch (provider) {
  1995. case "github":
  1996. $(
  1997. ".oauth2_token_url input, .oauth2_auth_url input, .oauth2_profile_url input, .oauth2_email_url input"
  1998. ).attr("required", "required");
  1999. $(
  2000. ".oauth2_token_url, .oauth2_auth_url, .oauth2_profile_url, .oauth2_email_url"
  2001. ).show();
  2002. break;
  2003. case "nextcloud":
  2004. case "gitea":
  2005. case "gitlab":
  2006. $(
  2007. ".oauth2_token_url input, .oauth2_auth_url input, .oauth2_profile_url input"
  2008. ).attr("required", "required");
  2009. $(".oauth2_token_url, .oauth2_auth_url, .oauth2_profile_url").show();
  2010. $("#oauth2_email_url").val("");
  2011. break;
  2012. }
  2013. }
  2014. }
  2015. // New authentication
  2016. if ($(".admin.new.authentication").length > 0) {
  2017. $("#auth_type").on("change", function () {
  2018. $(
  2019. ".ldap, .dldap, .smtp, .pam, .oauth2, .has-tls, .search-page-size, .sspi"
  2020. ).hide();
  2021. $(
  2022. ".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]"
  2023. ).removeAttr("required");
  2024. $(".binddnrequired").removeClass("required");
  2025. const authType = $(this).val();
  2026. switch (authType) {
  2027. case "2": // LDAP
  2028. $(".ldap").show();
  2029. $(".binddnrequired input, .ldap div.required:not(.dldap) input").attr(
  2030. "required",
  2031. "required"
  2032. );
  2033. $(".binddnrequired").addClass("required");
  2034. break;
  2035. case "3": // SMTP
  2036. $(".smtp").show();
  2037. $(".has-tls").show();
  2038. $(".smtp div.required input, .has-tls").attr("required", "required");
  2039. break;
  2040. case "4": // PAM
  2041. $(".pam").show();
  2042. $(".pam input").attr("required", "required");
  2043. break;
  2044. case "5": // LDAP
  2045. $(".dldap").show();
  2046. $(".dldap div.required:not(.ldap) input").attr(
  2047. "required",
  2048. "required"
  2049. );
  2050. break;
  2051. case "6": // OAuth2
  2052. $(".oauth2").show();
  2053. $(
  2054. ".oauth2 div.required:not(.oauth2_use_custom_url,.oauth2_use_custom_url_field,.open_id_connect_auto_discovery_url) input"
  2055. ).attr("required", "required");
  2056. onOAuth2Change();
  2057. break;
  2058. case "7": // SSPI
  2059. $(".sspi").show();
  2060. $(".sspi div.required input").attr("required", "required");
  2061. break;
  2062. }
  2063. if (authType === "2" || authType === "5") {
  2064. onSecurityProtocolChange();
  2065. }
  2066. if (authType === "2") {
  2067. onUsePagedSearchChange();
  2068. }
  2069. });
  2070. $("#auth_type").trigger("change");
  2071. $("#security_protocol").on("change", onSecurityProtocolChange);
  2072. $("#use_paged_search").on("change", onUsePagedSearchChange);
  2073. $("#oauth2_provider").on("change", onOAuth2Change);
  2074. $("#oauth2_use_custom_url").on("change", onOAuth2UseCustomURLChange);
  2075. }
  2076. // Edit authentication
  2077. if ($(".admin.edit.authentication").length > 0) {
  2078. const authType = $("#auth_type").val();
  2079. if (authType === "2" || authType === "5") {
  2080. $("#security_protocol").on("change", onSecurityProtocolChange);
  2081. if (authType === "2") {
  2082. $("#use_paged_search").on("change", onUsePagedSearchChange);
  2083. }
  2084. } else if (authType === "6") {
  2085. $("#oauth2_provider").on("change", onOAuth2Change);
  2086. $("#oauth2_use_custom_url").on("change", onOAuth2UseCustomURLChange);
  2087. onOAuth2Change();
  2088. }
  2089. }
  2090. // Notice
  2091. if ($(".admin.notice")) {
  2092. const $detailModal = $("#detail-modal");
  2093. // Attach view detail modals
  2094. $(".view-detail").on("click", function () {
  2095. $detailModal.find(".content pre").text($(this).data("content"));
  2096. $detailModal.modal("show");
  2097. return false;
  2098. });
  2099. // Select actions
  2100. const $checkboxes = $(".select.table .ui.checkbox");
  2101. $(".select.action").on("click", function () {
  2102. switch ($(this).data("action")) {
  2103. case "select-all":
  2104. $checkboxes.checkbox("check");
  2105. break;
  2106. case "deselect-all":
  2107. $checkboxes.checkbox("uncheck");
  2108. break;
  2109. case "inverse":
  2110. $checkboxes.checkbox("toggle");
  2111. break;
  2112. }
  2113. });
  2114. $("#delete-selection").on("click", function () {
  2115. const $this = $(this);
  2116. $this.addClass("loading disabled");
  2117. const ids = [];
  2118. $checkboxes.each(function () {
  2119. if ($(this).checkbox("is checked")) {
  2120. ids.push($(this).data("id"));
  2121. }
  2122. });
  2123. $.post($this.data("link"), {
  2124. _csrf: csrf,
  2125. ids,
  2126. }).done(() => {
  2127. window.location.href = $this.data("redirect");
  2128. });
  2129. });
  2130. }
  2131. }
  2132. function buttonsClickOnEnter() {
  2133. $(".ui.button").on("keypress", function (e) {
  2134. if (e.keyCode === 13 || e.keyCode === 32) {
  2135. // enter key or space bar
  2136. $(this).trigger("click");
  2137. }
  2138. });
  2139. }
  2140. function searchUsers() {
  2141. const $searchUserBox = $("#search-user-box");
  2142. $searchUserBox.search({
  2143. minCharacters: 2,
  2144. apiSettings: {
  2145. url: `${AppSubUrl}/api/v1/users/search?q={query}`,
  2146. onResponse(response) {
  2147. const items = [];
  2148. $.each(response.data, (_i, item) => {
  2149. let title = item.login;
  2150. if (item.full_name && item.full_name.length > 0) {
  2151. title += ` (${htmlEncode(item.full_name)})`;
  2152. }
  2153. items.push({
  2154. title,
  2155. image: item.avatar_url,
  2156. });
  2157. });
  2158. return { results: items };
  2159. },
  2160. },
  2161. searchFields: ["login", "full_name"],
  2162. showNoResults: false,
  2163. });
  2164. }
  2165. function searchTeams() {
  2166. const $searchTeamBox = $("#search-team-box");
  2167. $searchTeamBox.search({
  2168. minCharacters: 2,
  2169. apiSettings: {
  2170. url: `${AppSubUrl}/api/v1/orgs/${$searchTeamBox.data(
  2171. "org"
  2172. )}/teams/search?q={query}`,
  2173. headers: { "X-Csrf-Token": csrf },
  2174. onResponse(response) {
  2175. const items = [];
  2176. $.each(response.data, (_i, item) => {
  2177. const title = `${item.name} (${item.permission} access)`;
  2178. items.push({
  2179. title,
  2180. });
  2181. });
  2182. return { results: items };
  2183. },
  2184. },
  2185. searchFields: ["name", "description"],
  2186. showNoResults: false,
  2187. });
  2188. }
  2189. function searchRepositories() {
  2190. const $searchRepoBox = $("#search-repo-box");
  2191. $searchRepoBox.search({
  2192. minCharacters: 2,
  2193. apiSettings: {
  2194. url: `${AppSubUrl}/api/v1/repos/search?q={query}&uid=${$searchRepoBox.data(
  2195. "uid"
  2196. )}`,
  2197. onResponse(response) {
  2198. const items = [];
  2199. $.each(response.data, (_i, item) => {
  2200. items.push({
  2201. title: item.full_display_name.split("/")[1],
  2202. description: item.full_display_name,
  2203. });
  2204. });
  2205. return { results: items };
  2206. },
  2207. },
  2208. searchFields: ["full_name"],
  2209. showNoResults: false,
  2210. });
  2211. }
  2212. function initCodeView() {
  2213. if ($(".code-view .linenums").length > 0) {
  2214. $(document).on("click", ".lines-num span", function (e) {
  2215. const $select = $(this);
  2216. const $list = $select
  2217. .parent()
  2218. .siblings(".lines-code")
  2219. .find("ol.linenums > li");
  2220. selectRange(
  2221. $list,
  2222. $list.filter(`[rel=${$select.attr("id")}]`),
  2223. e.shiftKey ? $list.filter(".active").eq(0) : null
  2224. );
  2225. deSelect();
  2226. });
  2227. $(window)
  2228. .on("hashchange", () => {
  2229. let m = window.location.hash.match(/^#(L\d+)-(L\d+)$/);
  2230. const $list = $(".code-view ol.linenums > li");
  2231. let $first;
  2232. if (m) {
  2233. $first = $list.filter(`.${m[1]}`);
  2234. selectRange($list, $first, $list.filter(`.${m[2]}`));
  2235. $("html, body").scrollTop($first.offset().top - 200);
  2236. return;
  2237. }
  2238. m = window.location.hash.match(/^#(L|n)(\d+)$/);
  2239. if (m) {
  2240. $first = $list.filter(`.L${m[2]}`);
  2241. selectRange($list, $first);
  2242. $("html, body").scrollTop($first.offset().top - 200);
  2243. }
  2244. })
  2245. .trigger("hashchange");
  2246. }
  2247. $(".fold-code").on("click", ({ target }) => {
  2248. const box = target.closest(".file-content");
  2249. const folded = box.dataset.folded !== "true";
  2250. target.classList.add(`fa-chevron-${folded ? "right" : "down"}`);
  2251. target.classList.remove(`fa-chevron-${folded ? "down" : "right"}`);
  2252. box.dataset.folded = String(folded);
  2253. });
  2254. function insertBlobExcerpt(e) {
  2255. const $blob = $(e.target);
  2256. const $row = $blob.parent().parent();
  2257. $.get(
  2258. `${$blob.data("url")}?${$blob.data("query")}&anchor=${$blob.data(
  2259. "anchor"
  2260. )}`,
  2261. (blob) => {
  2262. $row.replaceWith(blob);
  2263. $(`[data-anchor="${$blob.data("anchor")}"]`).on("click", (e) => {
  2264. insertBlobExcerpt(e);
  2265. });
  2266. $(".diff-detail-box.ui.sticky").sticky();
  2267. }
  2268. );
  2269. }
  2270. $(".ui.blob-excerpt").on("click", (e) => {
  2271. insertBlobExcerpt(e);
  2272. });
  2273. }
  2274. function initU2FAuth() {
  2275. if ($("#wait-for-key").length === 0) {
  2276. return;
  2277. }
  2278. u2fApi
  2279. .ensureSupport()
  2280. .then(() => {
  2281. $.getJSON(`${AppSubUrl}/user/u2f/challenge`).done((req) => {
  2282. u2fApi
  2283. .sign(req.appId, req.challenge, req.registeredKeys, 30)
  2284. .then(u2fSigned)
  2285. .catch((err) => {
  2286. if (err === undefined) {
  2287. u2fError(1);
  2288. return;
  2289. }
  2290. u2fError(err.metaData.code);
  2291. });
  2292. });
  2293. })
  2294. .catch(() => {
  2295. // Fallback in case browser do not support U2F
  2296. window.location.href = `${AppSubUrl}/user/two_factor`;
  2297. });
  2298. }
  2299. function u2fSigned(resp) {
  2300. $.ajax({
  2301. url: `${AppSubUrl}/user/u2f/sign`,
  2302. type: "POST",
  2303. headers: { "X-Csrf-Token": csrf },
  2304. data: JSON.stringify(resp),
  2305. contentType: "application/json; charset=utf-8",
  2306. })
  2307. .done((res) => {
  2308. window.location.replace(res);
  2309. })
  2310. .fail(() => {
  2311. u2fError(1);
  2312. });
  2313. }
  2314. function u2fRegistered(resp) {
  2315. if (checkError(resp)) {
  2316. return;
  2317. }
  2318. $.ajax({
  2319. url: `${AppSubUrl}/user/settings/security/u2f/register`,
  2320. type: "POST",
  2321. headers: { "X-Csrf-Token": csrf },
  2322. data: JSON.stringify(resp),
  2323. contentType: "application/json; charset=utf-8",
  2324. success() {
  2325. reload();
  2326. },
  2327. fail() {
  2328. u2fError(1);
  2329. },
  2330. });
  2331. }
  2332. function checkError(resp) {
  2333. if (!("errorCode" in resp)) {
  2334. return false;
  2335. }
  2336. if (resp.errorCode === 0) {
  2337. return false;
  2338. }
  2339. u2fError(resp.errorCode);
  2340. return true;
  2341. }
  2342. function u2fError(errorType) {
  2343. const u2fErrors = {
  2344. browser: $("#unsupported-browser"),
  2345. 1: $("#u2f-error-1"),
  2346. 2: $("#u2f-error-2"),
  2347. 3: $("#u2f-error-3"),
  2348. 4: $("#u2f-error-4"),
  2349. 5: $(".u2f-error-5"),
  2350. };
  2351. u2fErrors[errorType].removeClass("hide");
  2352. Object.keys(u2fErrors).forEach((type) => {
  2353. if (type !== errorType) {
  2354. u2fErrors[type].addClass("hide");
  2355. }
  2356. });
  2357. $("#u2f-error").modal("show");
  2358. }
  2359. function initU2FRegister() {
  2360. $("#register-device").modal({ allowMultiple: false });
  2361. $("#u2f-error").modal({ allowMultiple: false });
  2362. $("#register-security-key").on("click", (e) => {
  2363. e.preventDefault();
  2364. u2fApi
  2365. .ensureSupport()
  2366. .then(u2fRegisterRequest)
  2367. .catch(() => {
  2368. u2fError("browser");
  2369. });
  2370. });
  2371. }
  2372. function u2fRegisterRequest() {
  2373. $.post(`${AppSubUrl}/user/settings/security/u2f/request_register`, {
  2374. _csrf: csrf,
  2375. name: $("#nickname").val(),
  2376. })
  2377. .done((req) => {
  2378. $("#nickname").closest("div.field").removeClass("error");
  2379. $("#register-device").modal("show");
  2380. if (req.registeredKeys === null) {
  2381. req.registeredKeys = [];
  2382. }
  2383. u2fApi
  2384. .register(req.appId, req.registerRequests, req.registeredKeys, 30)
  2385. .then(u2fRegistered)
  2386. .catch((reason) => {
  2387. if (reason === undefined) {
  2388. u2fError(1);
  2389. return;
  2390. }
  2391. u2fError(reason.metaData.code);
  2392. });
  2393. })
  2394. .fail((xhr) => {
  2395. if (xhr.status === 409) {
  2396. $("#nickname").closest("div.field").addClass("error");
  2397. }
  2398. });
  2399. }
  2400. function initWipTitle() {
  2401. $(".title_wip_desc > a").on("click", (e) => {
  2402. e.preventDefault();
  2403. const $issueTitle = $("#issue_title");
  2404. $issueTitle.focus();
  2405. const value = $issueTitle.val().trim().toUpperCase();
  2406. for (const i in wipPrefixes) {
  2407. if (value.startsWith(wipPrefixes[i].toUpperCase())) {
  2408. return;
  2409. }
  2410. }
  2411. $issueTitle.val(`${wipPrefixes[0]} ${$issueTitle.val()}`);
  2412. });
  2413. }
  2414. function initTemplateSearch() {
  2415. const $repoTemplate = $("#repo_template");
  2416. const checkTemplate = function () {
  2417. const $templateUnits = $("#template_units");
  2418. const $nonTemplate = $("#non_template");
  2419. if ($repoTemplate.val() !== "" && $repoTemplate.val() !== "0") {
  2420. $templateUnits.show();
  2421. $nonTemplate.hide();
  2422. } else {
  2423. $templateUnits.hide();
  2424. $nonTemplate.show();
  2425. }
  2426. };
  2427. $repoTemplate.on("change", checkTemplate);
  2428. checkTemplate();
  2429. const changeOwner = function () {
  2430. $("#repo_template_search").dropdown({
  2431. apiSettings: {
  2432. url: `${AppSubUrl}/api/v1/repos/search?q={query}&template=true&priority_owner_id=${$(
  2433. "#uid"
  2434. ).val()}`,
  2435. onResponse(response) {
  2436. const filteredResponse = { success: true, results: [] };
  2437. filteredResponse.results.push({
  2438. name: "",
  2439. value: "",
  2440. });
  2441. // Parse the response from the api to work with our dropdown
  2442. $.each(response.data, (_r, repo) => {
  2443. filteredResponse.results.push({
  2444. name: htmlEncode(repo.full_display_name),
  2445. value: repo.id,
  2446. });
  2447. });
  2448. return filteredResponse;
  2449. },
  2450. cache: false,
  2451. },
  2452. fullTextSearch: true,
  2453. });
  2454. };
  2455. $("#uid").on("change", changeOwner);
  2456. changeOwner();
  2457. }
  2458. $(document).ready(async () => {
  2459. // Show exact time
  2460. $(".time-since").each(function () {
  2461. $(this)
  2462. .addClass("poping up")
  2463. .attr("data-content", $(this).attr("title"))
  2464. .attr("data-variation", "inverted tiny")
  2465. .attr("title", "");
  2466. });
  2467. // Semantic UI modules.
  2468. $(".dropdown:not(.custom)").dropdown();
  2469. $(".jump.dropdown").dropdown({
  2470. action: "hide",
  2471. onShow() {
  2472. $(".poping.up").popup("hide");
  2473. },
  2474. });
  2475. $(".slide.up.dropdown").dropdown({
  2476. transition: "slide up",
  2477. });
  2478. $(".upward.dropdown").dropdown({
  2479. direction: "upward",
  2480. });
  2481. $(".ui.accordion").accordion();
  2482. $(".ui.checkbox").checkbox();
  2483. $(".ui.progress").progress({
  2484. showActivity: false,
  2485. });
  2486. $(".poping.up").popup();
  2487. $(".top.menu .poping.up").popup({
  2488. onShow() {
  2489. if ($(".top.menu .menu.transition").hasClass("visible")) {
  2490. return false;
  2491. }
  2492. },
  2493. });
  2494. $(".tabular.menu .item").tab();
  2495. $(".tabable.menu .item").tab();
  2496. $(".toggle.button").on("click", function () {
  2497. $($(this).data("target")).slideToggle(100);
  2498. });
  2499. // make table <tr> element clickable like a link
  2500. $("tr[data-href]").on("click", function () {
  2501. window.location = $(this).data("href");
  2502. });
  2503. // make table <td> element clickable like a link
  2504. $("td[data-href]").click(function () {
  2505. window.location = $(this).data("href");
  2506. });
  2507. // 在String原型对象上添加format方法
  2508. String.prototype.format = function () {
  2509. let str = this;
  2510. if (arguments.length == 0) {
  2511. return str;
  2512. } else {
  2513. Object.keys(arguments).forEach((item, index) => {
  2514. str = str.replace(/\?/, arguments[item]);
  2515. });
  2516. return str;
  2517. }
  2518. };
  2519. // Dropzone
  2520. const $dropzone = $("#dropzone");
  2521. if ($dropzone.length > 0) {
  2522. const filenameDict = {};
  2523. let maxFileTooltips;
  2524. let maxSizeTooltips;
  2525. if (
  2526. $dropzone.data("max-file-tooltips") &&
  2527. $dropzone.data("max-size-tooltips")
  2528. ) {
  2529. maxFileTooltips = $dropzone
  2530. .data("max-file-tooltips")
  2531. .format($dropzone.data("max-file"), $dropzone.data("max-size"));
  2532. maxSizeTooltips = $dropzone
  2533. .data("max-size-tooltips")
  2534. .format($dropzone.data("max-file"));
  2535. }
  2536. await createDropzone("#dropzone", {
  2537. url: $dropzone.data("upload-url"),
  2538. headers: { "X-Csrf-Token": csrf },
  2539. maxFiles: $dropzone.data("max-file"),
  2540. maxFilesize: $dropzone.data("max-size"),
  2541. acceptedFiles:
  2542. $dropzone.data("accepts") === "*/*" ? null : $dropzone.data("accepts"),
  2543. addRemoveLinks: true,
  2544. dictDefaultMessage: $dropzone.data("default-message"),
  2545. dictInvalidFileType: $dropzone.data("invalid-input-type"),
  2546. dictFileTooBig: $dropzone.data("file-too-big"),
  2547. dictRemoveFile: $dropzone.data("remove-file"),
  2548. init() {
  2549. this.on("success", (file, data) => {
  2550. filenameDict[file.name] = data.uuid;
  2551. const input = $(
  2552. `<input id="${data.uuid}" name="files" type="hidden">`
  2553. ).val(data.uuid);
  2554. $(".files").append(input);
  2555. });
  2556. this.on("removedfile", (file) => {
  2557. if (file.name in filenameDict) {
  2558. $(`#${filenameDict[file.name]}`).remove();
  2559. }
  2560. if ($dropzone.data("remove-url") && $dropzone.data("csrf")) {
  2561. $.post($dropzone.data("remove-url"), {
  2562. file: filenameDict[file.name],
  2563. _csrf: $dropzone.data("csrf"),
  2564. });
  2565. }
  2566. });
  2567. this.on("addedfile", (file) => {
  2568. if (file.size / (1000 * 1000) > $dropzone.data("max-size")) {
  2569. this.removeFile(file);
  2570. if (maxFileTooltips) {
  2571. $(".maxfilesize.ui.red.message").text(maxFileTooltips);
  2572. $(".maxfilesize.ui.red.message").css("display", "block");
  2573. }
  2574. } else {
  2575. if (maxFileTooltips) {
  2576. $(".maxfilesize.ui.red.message").css("display", "none");
  2577. }
  2578. }
  2579. });
  2580. this.on("maxfilesexceeded", (file) => {
  2581. this.removeFile(file);
  2582. if (maxSizeTooltips) {
  2583. $(".maxfilesize.ui.red.message").text(maxSizeTooltips);
  2584. $(".maxfilesize.ui.red.message").css("display", "block");
  2585. }
  2586. });
  2587. },
  2588. });
  2589. }
  2590. // Helpers.
  2591. $(".delete-button").on("click", showDeletePopup);
  2592. $(".add-all-button").on("click", showAddAllPopup);
  2593. $(".link-action").on("click", linkAction);
  2594. $(".link-email-action").on("click", linkEmailAction);
  2595. $(".delete-branch-button").on("click", showDeletePopup);
  2596. $(".undo-button").on("click", function () {
  2597. const $this = $(this);
  2598. $.post($this.data("url"), {
  2599. _csrf: csrf,
  2600. id: $this.data("id"),
  2601. }).done((data) => {
  2602. window.location.href = data.redirect;
  2603. });
  2604. });
  2605. $(".show-panel.button").on("click", function () {
  2606. $($(this).data("panel")).show();
  2607. });
  2608. $(".show-modal.button").on("click", function () {
  2609. $($(this).data("modal")).modal("show");
  2610. });
  2611. $(".delete-post.button").on("click", function () {
  2612. const $this = $(this);
  2613. $.post($this.data("request-url"), {
  2614. _csrf: csrf,
  2615. }).done(() => {
  2616. window.location.href = $this.data("done-url");
  2617. });
  2618. });
  2619. // Set anchor.
  2620. $(".markdown").each(function () {
  2621. $(this)
  2622. .find("h1, h2, h3, h4, h5, h6")
  2623. .each(function () {
  2624. let node = $(this);
  2625. node = node.wrap('<div class="anchor-wrap"></div>');
  2626. node.append(
  2627. `<a class="anchor" href="#${encodeURIComponent(
  2628. node.attr("id")
  2629. )}">${svg("octicon-link", 16)}</a>`
  2630. );
  2631. });
  2632. });
  2633. $(".issue-checkbox").on("click", () => {
  2634. const numChecked = $(".issue-checkbox").children("input:checked").length;
  2635. if (numChecked > 0) {
  2636. $("#issue-filters").addClass("hide");
  2637. $("#issue-actions").removeClass("hide");
  2638. } else {
  2639. $("#issue-filters").removeClass("hide");
  2640. $("#issue-actions").addClass("hide");
  2641. }
  2642. });
  2643. $(".issue-action").on("click", function () {
  2644. let { action } = this.dataset;
  2645. let { elementId } = this.dataset;
  2646. const issueIDs = $(".issue-checkbox")
  2647. .children("input:checked")
  2648. .map(function () {
  2649. return this.dataset.issueId;
  2650. })
  2651. .get()
  2652. .join();
  2653. console.log("this:", this);
  2654. const { url } = this.dataset;
  2655. if (elementId === "0" && url.substr(-9) === "/assignee") {
  2656. elementId = "";
  2657. action = "clear";
  2658. }
  2659. updateIssuesMeta(url, action, issueIDs, elementId, "").then(() => {
  2660. // NOTICE: This reset of checkbox state targets Firefox caching behaviour, as the checkboxes stay checked after reload
  2661. if (action === "close" || action === "open") {
  2662. // uncheck all checkboxes
  2663. $('.issue-checkbox input[type="checkbox"]').each((_, e) => {
  2664. e.checked = false;
  2665. });
  2666. }
  2667. reload();
  2668. });
  2669. });
  2670. // NOTICE: This event trigger targets Firefox caching behaviour, as the checkboxes stay checked after reload
  2671. // trigger ckecked event, if checkboxes are checked on load
  2672. $('.issue-checkbox input[type="checkbox"]:checked')
  2673. .first()
  2674. .each((_, e) => {
  2675. e.checked = false;
  2676. $(e).trigger("click");
  2677. });
  2678. $(".resolve-conversation").on("click", function (e) {
  2679. e.preventDefault();
  2680. const id = $(this).data("comment-id");
  2681. const action = $(this).data("action");
  2682. const url = $(this).data("update-url");
  2683. $.post(url, {
  2684. _csrf: csrf,
  2685. action,
  2686. comment_id: id,
  2687. }).then(reload);
  2688. });
  2689. buttonsClickOnEnter();
  2690. searchUsers();
  2691. searchTeams();
  2692. searchRepositories();
  2693. initCommentForm();
  2694. initInstall();
  2695. initRepository();
  2696. initMigration();
  2697. initWikiForm();
  2698. initEditForm();
  2699. initEditor();
  2700. initOrganization();
  2701. initGithook();
  2702. initWebhook();
  2703. initAdmin();
  2704. initCodeView();
  2705. initVueApp();
  2706. initVueUploader();
  2707. initVueDataset();
  2708. initVueEditAbout();
  2709. initVueEditTopic();
  2710. initVueContributors();
  2711. // initVueImages();
  2712. initVueModel();
  2713. initVueDataAnalysis();
  2714. initVueWxAutorize();
  2715. initVueselectDataset();
  2716. initVuereferenceDataset();
  2717. initTeamSettings();
  2718. initCtrlEnterSubmit();
  2719. initNavbarContentToggle();
  2720. // initTopicbar();vim
  2721. // closeTopicbar();
  2722. initU2FAuth();
  2723. initU2FRegister();
  2724. initIssueList();
  2725. initWipTitle();
  2726. initPullRequestReview();
  2727. initRepoStatusChecker();
  2728. initTemplateSearch();
  2729. initContextPopups();
  2730. initNotificationsTable();
  2731. initNotificationCount();
  2732. initTribute();
  2733. initDropDown();
  2734. initCloudrain();
  2735. initCloudrainSow();
  2736. initImage();
  2737. initContextMenu();
  2738. // Repo clone url.
  2739. if ($("#repo-clone-url").length > 0) {
  2740. switch (localStorage.getItem("repo-clone-protocol")) {
  2741. case "ssh":
  2742. if ($("#repo-clone-ssh").length === 0) {
  2743. $("#repo-clone-https").trigger("click");
  2744. }
  2745. break;
  2746. default:
  2747. $("#repo-clone-https").trigger("click");
  2748. break;
  2749. }
  2750. }
  2751. const routes = {
  2752. "div.user.settings": initUserSettings,
  2753. "div.repository.settings.collaboration": initRepositoryCollaboration,
  2754. };
  2755. let selector;
  2756. for (selector in routes) {
  2757. if ($(selector).length > 0) {
  2758. routes[selector]();
  2759. break;
  2760. }
  2761. }
  2762. const $cloneAddr = $("#clone_addr");
  2763. $cloneAddr.on("input change", () => {
  2764. const $repoName = $("#alias");
  2765. const $owner = $("#ownerDropdown div.text").attr("title");
  2766. const $urlAdd =
  2767. location.href.split("/")[0] + "//" + location.href.split("/")[2];
  2768. if ($cloneAddr.val().length > 0 /* && $repoName.val().length === 0 */) {
  2769. // modify when clone address change
  2770. const repoValue = $cloneAddr.val().match(/^(.*\/)?((.+?)(\.git)?)$/)[3];
  2771. $repoName.val($cloneAddr.val().match(/^(.*\/)?((.+?)(\.git)?)$/)[3]);
  2772. $.get(
  2773. `${window.config.AppSubUrl}/repo/check_name?q=${repoValue}&owner=${$owner}`,
  2774. (data) => {
  2775. const repo_name = data.name;
  2776. $("#repo_name").val(repo_name);
  2777. repo_name && $("#repo_name").parent().removeClass("error");
  2778. $("#repoAdress").css("display", "flex");
  2779. $("#repoAdress span").text(
  2780. $urlAdd + "/" + $owner + "/" + $("#repo_name").val() + ".git"
  2781. );
  2782. $("#repo_name").attr("placeholder", "");
  2783. }
  2784. );
  2785. }
  2786. });
  2787. // parallel init of lazy-loaded features
  2788. await Promise.all([
  2789. highlight(document.querySelectorAll("pre code")),
  2790. initGitGraph(),
  2791. initClipboard(),
  2792. initUserHeatmap(),
  2793. ]);
  2794. });
  2795. function changeHash(hash) {
  2796. if (window.history.pushState) {
  2797. window.history.pushState(null, null, hash);
  2798. } else {
  2799. window.location.hash = hash;
  2800. }
  2801. }
  2802. function deSelect() {
  2803. if (window.getSelection) {
  2804. window.getSelection().removeAllRanges();
  2805. } else {
  2806. document.selection.empty();
  2807. }
  2808. }
  2809. function selectRange($list, $select, $from) {
  2810. $list.removeClass("active");
  2811. if ($from) {
  2812. let a = parseInt($select.attr("rel").substr(1));
  2813. let b = parseInt($from.attr("rel").substr(1));
  2814. let c;
  2815. if (a !== b) {
  2816. if (a > b) {
  2817. c = a;
  2818. a = b;
  2819. b = c;
  2820. }
  2821. const classes = [];
  2822. for (let i = a; i <= b; i++) {
  2823. classes.push(`.L${i}`);
  2824. }
  2825. $list.filter(classes.join(",")).addClass("active");
  2826. changeHash(`#L${a}-L${b}`);
  2827. return;
  2828. }
  2829. }
  2830. $select.addClass("active");
  2831. changeHash(`#${$select.attr("rel")}`);
  2832. }
  2833. $(() => {
  2834. // Warn users that try to leave a page after entering data into a form.
  2835. // Except on sign-in pages, and for forms marked as 'ignore-dirty'.
  2836. if ($(".user.signin").length === 0) {
  2837. $("form:not(.ignore-dirty)").areYouSure();
  2838. }
  2839. // Parse SSH Key
  2840. $("#ssh-key-content").on("change paste keyup", function () {
  2841. const arrays = $(this).val().split(" ");
  2842. const $title = $("#ssh-key-title");
  2843. if ($title.val() === "" && arrays.length === 3 && arrays[2] !== "") {
  2844. $title.val(arrays[2]);
  2845. }
  2846. });
  2847. });
  2848. function showDeletePopup() {
  2849. const $this = $(this);
  2850. let filter = "";
  2851. if ($this.attr("id")) {
  2852. filter += `#${$this.attr("id")}`;
  2853. }
  2854. const dialog = $(`.delete.modal${filter}`);
  2855. dialog.find(".name").text($this.data("name"));
  2856. dialog
  2857. .modal({
  2858. closable: false,
  2859. onApprove() {
  2860. if ($this.data("type") === "form") {
  2861. $($this.data("form")).trigger("submit");
  2862. return;
  2863. }
  2864. $.post($this.data("url"), {
  2865. _csrf: csrf,
  2866. id: $this.data("id"),
  2867. }).done((data) => {
  2868. window.location.href = data.redirect;
  2869. });
  2870. },
  2871. })
  2872. .modal("show");
  2873. return false;
  2874. }
  2875. function showAddAllPopup() {
  2876. const $this = $(this);
  2877. let filter = "";
  2878. if ($this.attr("id")) {
  2879. filter += `#${$this.attr("id")}`;
  2880. }
  2881. const dialog = $(`.addall.modal${filter}`);
  2882. dialog.find(".name").text($this.data("name"));
  2883. dialog
  2884. .modal({
  2885. closable: false,
  2886. onApprove() {
  2887. if ($this.data("type") === "form") {
  2888. $($this.data("form")).trigger("submit");
  2889. return;
  2890. }
  2891. $.post($this.data("url"), {
  2892. _csrf: csrf,
  2893. id: $this.data("id"),
  2894. }).done((data) => {
  2895. window.location.href = data.redirect;
  2896. });
  2897. },
  2898. })
  2899. .modal("show");
  2900. return false;
  2901. }
  2902. function linkAction(e) {
  2903. e.preventDefault();
  2904. const $this = $(this);
  2905. const redirect = $this.data("redirect");
  2906. $.post($this.data("url"), {
  2907. _csrf: csrf,
  2908. }).done((data) => {
  2909. if (data.redirect) {
  2910. window.location.href = data.redirect;
  2911. } else if (redirect) {
  2912. window.location.href = redirect;
  2913. } else {
  2914. window.location.reload();
  2915. }
  2916. });
  2917. }
  2918. function linkEmailAction(e) {
  2919. const $this = $(this);
  2920. $("#form-uid").val($this.data("uid"));
  2921. $("#form-email").val($this.data("email"));
  2922. $("#form-primary").val($this.data("primary"));
  2923. $("#form-activate").val($this.data("activate"));
  2924. $("#form-uid").val($this.data("uid"));
  2925. $("#change-email-modal").modal("show");
  2926. e.preventDefault();
  2927. }
  2928. function initVueComponents() {
  2929. const vueDelimeters = ["${", "}"];
  2930. Vue.component("repo-search", {
  2931. delimiters: vueDelimeters,
  2932. props: {
  2933. searchLimit: {
  2934. type: Number,
  2935. default: 10,
  2936. },
  2937. suburl: {
  2938. type: String,
  2939. required: true,
  2940. },
  2941. uid: {
  2942. type: Number,
  2943. required: true,
  2944. },
  2945. organizations: {
  2946. type: Array,
  2947. default: [],
  2948. },
  2949. isOrganization: {
  2950. type: Boolean,
  2951. default: true,
  2952. },
  2953. canCreateOrganization: {
  2954. type: Boolean,
  2955. default: false,
  2956. },
  2957. organizationsTotalCount: {
  2958. type: Number,
  2959. default: 0,
  2960. },
  2961. moreReposLink: {
  2962. type: String,
  2963. default: "",
  2964. },
  2965. },
  2966. data() {
  2967. const params = new URLSearchParams(window.location.search);
  2968. let tab = params.get("repo-search-tab");
  2969. if (!tab) {
  2970. tab = "repos";
  2971. }
  2972. let reposFilter = params.get("repo-search-filter");
  2973. if (!reposFilter) {
  2974. reposFilter = "all";
  2975. }
  2976. let privateFilter = params.get("repo-search-private");
  2977. if (!privateFilter) {
  2978. privateFilter = "both";
  2979. }
  2980. let archivedFilter = params.get("repo-search-archived");
  2981. if (!archivedFilter) {
  2982. archivedFilter = "unarchived";
  2983. }
  2984. let searchQuery = params.get("repo-search-query");
  2985. if (!searchQuery) {
  2986. searchQuery = "";
  2987. }
  2988. let page = 1;
  2989. try {
  2990. page = parseInt(params.get("repo-search-page"));
  2991. } catch {
  2992. // noop
  2993. }
  2994. if (!page) {
  2995. page = 1;
  2996. }
  2997. return {
  2998. tab,
  2999. repos: [],
  3000. reposTotalCount: 0,
  3001. reposFilter,
  3002. archivedFilter,
  3003. privateFilter,
  3004. page,
  3005. finalPage: 1,
  3006. searchQuery,
  3007. isLoading: false,
  3008. staticPrefix: StaticUrlPrefix,
  3009. counts: {},
  3010. repoTypes: {
  3011. all: {
  3012. searchMode: "",
  3013. },
  3014. forks: {
  3015. searchMode: "fork",
  3016. },
  3017. mirrors: {
  3018. searchMode: "mirror",
  3019. },
  3020. sources: {
  3021. searchMode: "source",
  3022. },
  3023. collaborative: {
  3024. searchMode: "collaborative",
  3025. },
  3026. },
  3027. };
  3028. },
  3029. computed: {
  3030. showMoreReposLink() {
  3031. return (
  3032. this.repos.length > 0 &&
  3033. this.repos.length <
  3034. this.counts[
  3035. `${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`
  3036. ]
  3037. );
  3038. },
  3039. searchURL() {
  3040. return `${
  3041. this.suburl
  3042. }/api/v1/repos/search?sort=updated&order=desc&uid=${this.uid}&q=${
  3043. this.searchQuery
  3044. }&page=${this.page}&limit=${this.searchLimit}&mode=${
  3045. this.repoTypes[this.reposFilter].searchMode
  3046. }${this.reposFilter !== "all" ? "&exclusive=1" : ""}${
  3047. this.archivedFilter === "archived" ? "&archived=true" : ""
  3048. }${this.archivedFilter === "unarchived" ? "&archived=false" : ""}${
  3049. this.privateFilter === "private" ? "&onlyPrivate=true" : ""
  3050. }${this.privateFilter === "public" ? "&private=false" : ""}`;
  3051. },
  3052. repoTypeCount() {
  3053. return this.counts[
  3054. `${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`
  3055. ];
  3056. },
  3057. },
  3058. mounted() {
  3059. this.searchRepos(this.reposFilter);
  3060. $(this.$el).find(".poping.up").popup();
  3061. $(this.$el).find(".dropdown").dropdown();
  3062. this.setCheckboxes();
  3063. const self = this;
  3064. Vue.nextTick(() => {
  3065. self.$refs.search.focus();
  3066. });
  3067. },
  3068. methods: {
  3069. changeTab(t) {
  3070. this.tab = t;
  3071. this.updateHistory();
  3072. },
  3073. setCheckboxes() {
  3074. switch (this.archivedFilter) {
  3075. case "unarchived":
  3076. $("#archivedFilterCheckbox").checkbox("set unchecked");
  3077. break;
  3078. case "archived":
  3079. $("#archivedFilterCheckbox").checkbox("set checked");
  3080. break;
  3081. case "both":
  3082. $("#archivedFilterCheckbox").checkbox("set indeterminate");
  3083. break;
  3084. default:
  3085. this.archivedFilter = "unarchived";
  3086. $("#archivedFilterCheckbox").checkbox("set unchecked");
  3087. break;
  3088. }
  3089. switch (this.privateFilter) {
  3090. case "public":
  3091. $("#privateFilterCheckbox").checkbox("set unchecked");
  3092. break;
  3093. case "private":
  3094. $("#privateFilterCheckbox").checkbox("set checked");
  3095. break;
  3096. case "both":
  3097. $("#privateFilterCheckbox").checkbox("set indeterminate");
  3098. break;
  3099. default:
  3100. this.privateFilter = "both";
  3101. $("#privateFilterCheckbox").checkbox("set indeterminate");
  3102. break;
  3103. }
  3104. },
  3105. changeReposFilter(filter) {
  3106. this.reposFilter = filter;
  3107. this.repos = [];
  3108. this.page = 1;
  3109. Vue.set(
  3110. this.counts,
  3111. `${filter}:${this.archivedFilter}:${this.privateFilter}`,
  3112. 0
  3113. );
  3114. this.searchRepos();
  3115. },
  3116. updateHistory() {
  3117. const params = new URLSearchParams(window.location.search);
  3118. if (this.tab === "repos") {
  3119. params.delete("repo-search-tab");
  3120. } else {
  3121. params.set("repo-search-tab", this.tab);
  3122. }
  3123. if (this.reposFilter === "all") {
  3124. params.delete("repo-search-filter");
  3125. } else {
  3126. params.set("repo-search-filter", this.reposFilter);
  3127. }
  3128. if (this.privateFilter === "both") {
  3129. params.delete("repo-search-private");
  3130. } else {
  3131. params.set("repo-search-private", this.privateFilter);
  3132. }
  3133. if (this.archivedFilter === "unarchived") {
  3134. params.delete("repo-search-archived");
  3135. } else {
  3136. params.set("repo-search-archived", this.archivedFilter);
  3137. }
  3138. if (this.searchQuery === "") {
  3139. params.delete("repo-search-query");
  3140. } else {
  3141. params.set("repo-search-query", this.searchQuery);
  3142. }
  3143. if (this.page === 1) {
  3144. params.delete("repo-search-page");
  3145. } else {
  3146. params.set("repo-search-page", `${this.page}`);
  3147. }
  3148. window.history.replaceState({}, "", `?${params.toString()}`);
  3149. },
  3150. toggleArchivedFilter() {
  3151. switch (this.archivedFilter) {
  3152. case "both":
  3153. this.archivedFilter = "unarchived";
  3154. break;
  3155. case "unarchived":
  3156. this.archivedFilter = "archived";
  3157. break;
  3158. case "archived":
  3159. this.archivedFilter = "both";
  3160. break;
  3161. default:
  3162. this.archivedFilter = "unarchived";
  3163. break;
  3164. }
  3165. this.page = 1;
  3166. this.repos = [];
  3167. this.setCheckboxes();
  3168. Vue.set(
  3169. this.counts,
  3170. `${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`,
  3171. 0
  3172. );
  3173. this.searchRepos();
  3174. },
  3175. togglePrivateFilter() {
  3176. switch (this.privateFilter) {
  3177. case "both":
  3178. this.privateFilter = "public";
  3179. break;
  3180. case "public":
  3181. this.privateFilter = "private";
  3182. break;
  3183. case "private":
  3184. this.privateFilter = "both";
  3185. break;
  3186. default:
  3187. this.privateFilter = "both";
  3188. break;
  3189. }
  3190. this.page = 1;
  3191. this.repos = [];
  3192. this.setCheckboxes();
  3193. Vue.set(
  3194. this.counts,
  3195. `${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`,
  3196. 0
  3197. );
  3198. this.searchRepos();
  3199. },
  3200. changePage(page) {
  3201. this.page = page;
  3202. if (this.page > this.finalPage) {
  3203. this.page = this.finalPage;
  3204. }
  3205. if (this.page < 1) {
  3206. this.page = 1;
  3207. }
  3208. this.repos = [];
  3209. Vue.set(
  3210. this.counts,
  3211. `${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`,
  3212. 0
  3213. );
  3214. this.searchRepos();
  3215. },
  3216. showArchivedRepo(repo) {
  3217. switch (this.archivedFilter) {
  3218. case "both":
  3219. return true;
  3220. case "unarchived":
  3221. return !repo.archived;
  3222. case "archived":
  3223. return repo.archived;
  3224. default:
  3225. return !repo.archived;
  3226. }
  3227. },
  3228. showPrivateRepo(repo) {
  3229. switch (this.privateFilter) {
  3230. case "both":
  3231. return true;
  3232. case "public":
  3233. return !repo.private;
  3234. case "private":
  3235. return repo.private;
  3236. default:
  3237. return true;
  3238. }
  3239. },
  3240. showFilteredRepo(repo) {
  3241. switch (this.reposFilter) {
  3242. case "sources":
  3243. return repo.owner.id === this.uid && !repo.mirror && !repo.fork;
  3244. case "forks":
  3245. return repo.owner.id === this.uid && !repo.mirror && repo.fork;
  3246. case "mirrors":
  3247. return repo.mirror;
  3248. case "collaborative":
  3249. return repo.owner.id !== this.uid && !repo.mirror;
  3250. default:
  3251. return true;
  3252. }
  3253. },
  3254. showRepo(repo) {
  3255. return (
  3256. this.showArchivedRepo(repo) &&
  3257. this.showPrivateRepo(repo) &&
  3258. this.showFilteredRepo(repo)
  3259. );
  3260. },
  3261. searchRepos() {
  3262. const self = this;
  3263. this.isLoading = true;
  3264. const searchedMode = this.repoTypes[this.reposFilter].searchMode;
  3265. const searchedURL = this.searchURL;
  3266. const searchedQuery = this.searchQuery;
  3267. $.getJSON(searchedURL, (result, _textStatus, request) => {
  3268. if (searchedURL === self.searchURL) {
  3269. self.repos = result.data;
  3270. const count = request.getResponseHeader("X-Total-Count");
  3271. if (
  3272. searchedQuery === "" &&
  3273. searchedMode === "" &&
  3274. self.archivedFilter === "both"
  3275. ) {
  3276. self.reposTotalCount = count;
  3277. }
  3278. Vue.set(
  3279. self.counts,
  3280. `${self.reposFilter}:${self.archivedFilter}:${self.privateFilter}`,
  3281. count
  3282. );
  3283. self.finalPage = Math.ceil(count / self.searchLimit);
  3284. self.updateHistory();
  3285. }
  3286. }).always(() => {
  3287. if (searchedURL === self.searchURL) {
  3288. self.isLoading = false;
  3289. }
  3290. });
  3291. },
  3292. repoClass(repo) {
  3293. if (repo.fork) {
  3294. return "octicon-repo-forked";
  3295. }
  3296. if (repo.mirror) {
  3297. return "octicon-repo-clone";
  3298. }
  3299. if (repo.template) {
  3300. return `octicon-repo-template${repo.private ? "-private" : ""}`;
  3301. }
  3302. if (repo.private) {
  3303. return "octicon-lock";
  3304. }
  3305. return "octicon-repo";
  3306. },
  3307. },
  3308. });
  3309. }
  3310. function initCtrlEnterSubmit() {
  3311. $(".js-quick-submit").on("keydown", function (e) {
  3312. if (
  3313. ((e.ctrlKey && !e.altKey) || e.metaKey) &&
  3314. (e.keyCode === 13 || e.keyCode === 10)
  3315. ) {
  3316. $(this).closest("form").trigger("submit");
  3317. }
  3318. });
  3319. }
  3320. function initVueApp() {
  3321. const el = document.getElementById("app");
  3322. if (!el) {
  3323. return;
  3324. }
  3325. initVueComponents();
  3326. new Vue({
  3327. delimiters: ["${", "}"],
  3328. el,
  3329. data: {
  3330. page: parseInt(new URLSearchParams(window.location.search).get("page")),
  3331. searchLimit: Number(
  3332. (document.querySelector("meta[name=_search_limit]") || {}).content
  3333. ),
  3334. page: 1,
  3335. suburl: AppSubUrl,
  3336. uid: Number(
  3337. (document.querySelector("meta[name=_context_uid]") || {}).content
  3338. ),
  3339. activityTopAuthors: window.ActivityTopAuthors || [],
  3340. localHref: "",
  3341. },
  3342. components: {
  3343. ActivityTopAuthors,
  3344. },
  3345. mounted() {
  3346. this.page = parseInt(
  3347. new URLSearchParams(window.location.search).get("page")
  3348. );
  3349. this.localHref = location.href;
  3350. },
  3351. methods: {
  3352. handleCurrentChange: function (val) {
  3353. const searchParams = new URLSearchParams(window.location.search);
  3354. if (!window.location.search) {
  3355. window.location.href = this.localHref + "?page=" + val;
  3356. } else if (searchParams.has("page")) {
  3357. window.location.href = this.localHref.replace(
  3358. /page=[0-9]+/g,
  3359. "page=" + val
  3360. );
  3361. } else {
  3362. window.location.href = location.href + "&page=" + val;
  3363. }
  3364. this.page = val;
  3365. },
  3366. },
  3367. });
  3368. }
  3369. function initVueUploader() {
  3370. const el = document.getElementById("minioUploader");
  3371. if (!el) {
  3372. return;
  3373. }
  3374. new Vue({
  3375. el: "#minioUploader",
  3376. components: { MinioUploader },
  3377. template: "<MinioUploader />",
  3378. });
  3379. }
  3380. function initVueEditAbout() {
  3381. const el = document.getElementById("about-desc");
  3382. if (!el) {
  3383. return;
  3384. }
  3385. new Vue({
  3386. el: "#about-desc",
  3387. render: (h) => h(EditAboutInfo),
  3388. });
  3389. }
  3390. function initVueDataset() {
  3391. if ($("#dataset_check").length) {
  3392. if (location.search.indexOf("recommend=true") !== -1) {
  3393. $("#dataset_check").checkbox("set checked");
  3394. } else {
  3395. $("#dataset_check").checkbox("set unchecked");
  3396. }
  3397. $("#dataset_check").checkbox({
  3398. onChecked: function () {
  3399. if (location.search) {
  3400. const params = new URLSearchParams(location.search);
  3401. if (params.has("recommend")) {
  3402. params.delete("recommend");
  3403. location.href =
  3404. AppSubUrl +
  3405. location.pathname +
  3406. "?" +
  3407. params.toString() +
  3408. "&recommend=true";
  3409. } else {
  3410. location.href = `${window.config.AppSubUrl}/admin/datasets${location.search}&recommend=true`;
  3411. }
  3412. } else {
  3413. location.href = `${window.config.AppSubUrl}/admin/datasets?recommend=true`;
  3414. }
  3415. },
  3416. onUnchecked: function () {
  3417. if (location.search == "?recommend=true") {
  3418. location.href = AppSubUrl + location.pathname;
  3419. } else {
  3420. const params = new URLSearchParams(location.search);
  3421. params.delete("recommend");
  3422. location.href =
  3423. AppSubUrl + location.pathname + "?" + params.toString();
  3424. }
  3425. },
  3426. });
  3427. }
  3428. $(".set_dataset").on("click", function () {
  3429. const $this = $(this);
  3430. let link = $this.data("url");
  3431. $.ajax({
  3432. url: link,
  3433. type: "PUT",
  3434. success: function (res) {
  3435. console.log(res);
  3436. if (res.Code == 0) {
  3437. window.location.href = "/admin/datasets";
  3438. } else {
  3439. $(".ui.negative.message")
  3440. .text(res.Message)
  3441. .show()
  3442. .delay(1500)
  3443. .fadeOut();
  3444. }
  3445. },
  3446. error: function (xhr) {
  3447. // 隐藏 loading
  3448. // 只有请求不正常(状态码不为200)才会执行
  3449. $(".ui.negative.message")
  3450. .html(xhr.responseText)
  3451. .show()
  3452. .delay(1500)
  3453. .fadeOut();
  3454. console.log(xhr);
  3455. },
  3456. complete: function (xhr) {
  3457. // $("#mask").css({"display":"none","z-index":"1"})
  3458. },
  3459. });
  3460. });
  3461. const el = document.getElementById("dataset-base");
  3462. if (!el) {
  3463. return;
  3464. }
  3465. let link = $("#square-link").data("link");
  3466. let repolink = $(".dataset-repolink").data("repolink");
  3467. let datasetType = $(".dataset-repolink").data("dataset-type");
  3468. const clearBtn = document.getElementsByClassName("clear_dataset_value");
  3469. const params = new URLSearchParams(location.search);
  3470. for (let i = 0; i < clearBtn.length; i++) {
  3471. clearBtn[i].addEventListener("click", function (e) {
  3472. let searchType = e.target.getAttribute("data-clear-value");
  3473. if (params.has(searchType)) {
  3474. params.delete(searchType);
  3475. let clearSearch = params.toString();
  3476. location.href = link + "?" + clearSearch;
  3477. }
  3478. });
  3479. }
  3480. const items = [];
  3481. const zipStatus = [];
  3482. $("#dataset-range-value")
  3483. .find(".item")
  3484. .each(function () {
  3485. items.push($(this).data("private"));
  3486. zipStatus.push($(this).data("decompress-state"));
  3487. });
  3488. let num_stars = $("#dataset-range-value").data("num-stars");
  3489. let star_active = $("#dataset-range-value").data("star-active");
  3490. const ruleForm = {};
  3491. if (document.getElementById("dataset-edit-value")) {
  3492. let $this = $("#dataset-edit-value");
  3493. ruleForm.title = $this.data("edit-title") || "";
  3494. ruleForm.description = $this.data("edit-description") || "";
  3495. ruleForm.category = $this.data("edit-category") || "";
  3496. ruleForm.task = $this.data("edit-task") || "";
  3497. ruleForm.license = $this.data("edit-license") || "";
  3498. ruleForm.id = $this.data("edit-id") || "";
  3499. ruleForm._csrf = csrf;
  3500. }
  3501. const starItems = [];
  3502. const starActives = [];
  3503. $("#datasets-square-range-value")
  3504. .find(".item")
  3505. .each(function () {
  3506. starItems.push($(this).data("num-stars"));
  3507. starActives.push($(this).data("star-active"));
  3508. });
  3509. const taskLists = [];
  3510. const licenseLists = [];
  3511. $("#task-square-range-value")
  3512. .find(".item")
  3513. .each(function () {
  3514. taskLists.push($(this).data("task"));
  3515. });
  3516. $("#task-square-range-value")
  3517. .find(".item")
  3518. .each(function () {
  3519. licenseLists.push($(this).data("license"));
  3520. });
  3521. let dataset_file_desc;
  3522. if (document.getElementById("dataset-file-desc")) {
  3523. dataset_file_desc = document.getElementById("dataset-file-desc").value;
  3524. }
  3525. new Vue({
  3526. delimiters: ["${", "}"],
  3527. el,
  3528. data: {
  3529. suburl: AppSubUrl,
  3530. url: "",
  3531. checked: false,
  3532. clusterFlag: false,
  3533. type: 0,
  3534. desc: "",
  3535. descfile: "",
  3536. datasetType: "",
  3537. privates: [],
  3538. zipStatus: [],
  3539. starItems: [],
  3540. starActives: [],
  3541. taskLists: [],
  3542. taskShow: [],
  3543. licenseLists: [],
  3544. licenseShow: [],
  3545. hasMoreBthHis: false,
  3546. showMoreHis: false,
  3547. star_active: false,
  3548. num_stars: 0,
  3549. dialogVisible: false,
  3550. activeName: "first",
  3551. searchDataItem: "",
  3552. currentRepoDataset: [],
  3553. myDataset: [],
  3554. publicDataset: [],
  3555. myFavoriteDataset: [],
  3556. page: 1,
  3557. totalnums: 0,
  3558. repolink: "",
  3559. datasetType: 0,
  3560. dataset_uuid: "",
  3561. dataset_name: "",
  3562. loadingDataIndex: false,
  3563. timer: null,
  3564. ruleForm: {
  3565. title: "",
  3566. description: "",
  3567. category: "",
  3568. task: "",
  3569. license: "",
  3570. _csrf: csrf,
  3571. },
  3572. ruleForm1: {
  3573. title: "",
  3574. description: "",
  3575. category: "",
  3576. task: "",
  3577. license: "",
  3578. _csrf: "",
  3579. id: "",
  3580. },
  3581. rules: {
  3582. title: [
  3583. { required: true, message: "请输入数据集名称", trigger: "blur" },
  3584. {
  3585. min: 1,
  3586. max: 100,
  3587. message: "长度在 1 到 100 个字符",
  3588. trigger: "blur",
  3589. },
  3590. // {required:true,message:'test',pattern:'/^[a-zA-Z0-9-_]{1,100}[^-]$/',trigger:'blur'},
  3591. {
  3592. validator: (rule, value, callback) => {
  3593. if (/^[a-zA-Z0-9-_.]{0,100}$/.test(value) == false) {
  3594. callback(new Error("输入不符合数据集名称规则"));
  3595. } else {
  3596. callback();
  3597. }
  3598. },
  3599. trigger: "blur",
  3600. },
  3601. ],
  3602. description: [
  3603. { required: true, message: "请输入数据集描述详情", trigger: "blur" },
  3604. ],
  3605. category: [
  3606. { required: true, message: "请选择分类", trigger: "change" },
  3607. ],
  3608. task: [
  3609. {
  3610. required: true,
  3611. message: "请选择研究方向/应用领域",
  3612. trigger: "change",
  3613. },
  3614. ],
  3615. // license: [
  3616. // { required: true, message: '请选择活动区域', trigger: 'change' }
  3617. // ]
  3618. },
  3619. },
  3620. components: {
  3621. MinioUploader,
  3622. },
  3623. mounted() {
  3624. if (document.getElementById("fail_dataset_name")) {
  3625. this.dataset_name = document.getElementById("fail_dataset_name").value;
  3626. this.dataset_uuid = document.getElementById("fail_dataset_uuid").value;
  3627. }
  3628. this.getTypeList();
  3629. if (!!document.getElementById("dataset-repolink-init")) {
  3630. // this.datasetType = location.href.indexOf('cloudbrain') !== -1 ? 0 : 1
  3631. this.datasetType = $("#dataset-repolink-init").data("dataset-type");
  3632. this.getCurrentRepoDataset(this.repolink, this.datasetType);
  3633. }
  3634. const params = new URLSearchParams(location.search);
  3635. if (params.has("recommend") && params.get("recommend") == "true") {
  3636. this.checked = true;
  3637. } else {
  3638. this.checked = false;
  3639. }
  3640. },
  3641. created() {
  3642. if (document.getElementById("postPath")) {
  3643. this.url = document.getElementById("postPath").value;
  3644. }
  3645. this.privates = items;
  3646. this.zipStatus = zipStatus;
  3647. this.num_stars = num_stars;
  3648. this.star_active = star_active;
  3649. this.ruleForm1 = ruleForm;
  3650. // this.getEditInit()
  3651. this.starItems = starItems;
  3652. this.starActives = starActives;
  3653. this.taskLists = taskLists;
  3654. this.licenseLists = licenseLists;
  3655. this.descfile = dataset_file_desc;
  3656. this.repolink = repolink;
  3657. this.datasetType = datasetType;
  3658. },
  3659. methods: {
  3660. copyUrl(url) {
  3661. const cInput = document.createElement("input");
  3662. cInput.value = url;
  3663. document.body.appendChild(cInput);
  3664. cInput.select();
  3665. document.execCommand("Copy");
  3666. cInput.remove();
  3667. $("body").toast({
  3668. message: "复制成功!",
  3669. showProgress: "bottom",
  3670. showIcon: "check circle",
  3671. class: "info",
  3672. position: "top right",
  3673. });
  3674. },
  3675. handleCurrentChange(val) {
  3676. this.page = val;
  3677. switch (this.activeName) {
  3678. case "first":
  3679. this.getCurrentRepoDataset(this.repolink, this.datasetType);
  3680. break;
  3681. case "second":
  3682. this.getMyDataset(this.repolink, this.datasetType);
  3683. break;
  3684. case "third":
  3685. this.getPublicDataset(this.repolink, this.datasetType);
  3686. break;
  3687. case "fourth":
  3688. this.getStarDataset(this.repolink, this.datasetType);
  3689. break;
  3690. }
  3691. },
  3692. handleCheckedChange(val) {
  3693. if (val) {
  3694. if (location.search) {
  3695. const params = new URLSearchParams(location.search);
  3696. if (params.has("recommend")) {
  3697. params.delete("recommend");
  3698. let search = params.toString();
  3699. location.href = `${AppSubUrl}/explore/datasets?${search}&recommend=${val}`;
  3700. } else {
  3701. location.href = `${AppSubUrl}/explore/datasets${location.search}&recommend=${val}`;
  3702. }
  3703. } else {
  3704. location.href = `${AppSubUrl}/explore/datasets?recommend=${val}`;
  3705. }
  3706. } else {
  3707. if (location.search == "?recommend=true") {
  3708. location.href = AppSubUrl + location.pathname;
  3709. } else {
  3710. const params = new URLSearchParams(location.search);
  3711. params.delete("recommend");
  3712. location.href =
  3713. AppSubUrl + location.pathname + "?" + params.toString();
  3714. }
  3715. }
  3716. },
  3717. createDataset(formName) {
  3718. let _this = this;
  3719. this.$refs[formName].validate((valid) => {
  3720. if (valid) {
  3721. document.getElementById("mask").style.display = "block";
  3722. _this.$axios
  3723. .post(_this.url, _this.qs.stringify(_this.ruleForm))
  3724. .then((res) => {
  3725. if (res.data.Code === 0) {
  3726. document.getElementById("mask").style.display = "none";
  3727. location.href = _this.url.split("/create")[0] + "?type=-1";
  3728. } else {
  3729. console.log(res.data.Message);
  3730. }
  3731. document.getElementById("mask").style.display = "none";
  3732. })
  3733. .catch((error) => {
  3734. console.log(error);
  3735. });
  3736. } else {
  3737. return false;
  3738. }
  3739. });
  3740. },
  3741. cancelDataset(getpage, attachment) {
  3742. if (getpage && !attachment) {
  3743. if (getpage === "create") {
  3744. location.href = this.url.split("/create")[0] + "?type=-1";
  3745. } else if (getpage === "edit") {
  3746. location.href = this.url.split("/edit")[0] + "?type=-1";
  3747. } else {
  3748. location.href = "/";
  3749. }
  3750. } else {
  3751. location.href = `${AppSubUrl}${attachment}/datasets`;
  3752. }
  3753. },
  3754. gotoUpload(repolink, datsetId) {
  3755. // location.href = `${AppSubUrl}${repolink}/datasets/attachments/upload?datasetId=${datsetId}`
  3756. window.open(
  3757. `${AppSubUrl}${repolink}/datasets/attachments/upload?datasetId=${datsetId}`,
  3758. "_blank"
  3759. );
  3760. },
  3761. gotoDataset(datsetUrl) {
  3762. location.href = datsetUrl;
  3763. },
  3764. gotoAnnotate(repolink, uuid, type) {
  3765. location.href = `${AppSubUrl}${repolink}/datasets/label/${uuid}?type=${type}`;
  3766. },
  3767. setcluster(val) {
  3768. this.clusterFlag = val;
  3769. },
  3770. uploadGpu() {
  3771. this.type = 0;
  3772. },
  3773. uploadNpu() {
  3774. this.type = 1;
  3775. },
  3776. sortAble(dom) {
  3777. const params = new URLSearchParams(location.search);
  3778. if (params.toString() === "") {
  3779. location.href = `${location.href}?sort=${dom}Asc`;
  3780. } else if (!params.get("sort")) {
  3781. location.href = `${location.href}&sort=${dom}Asc`;
  3782. } else if (
  3783. params.get("sort") === `${dom}Desc` ||
  3784. params.get("sort").indexOf(`${dom}`) === -1
  3785. ) {
  3786. params.set("sort", `${dom}Asc`);
  3787. let asc = params.toString();
  3788. location.search = asc;
  3789. } else {
  3790. params.set("sort", `${dom}Desc`);
  3791. let desc = params.toString();
  3792. location.search = desc;
  3793. }
  3794. },
  3795. sortIcon(dom, sort) {
  3796. const params = new URLSearchParams(location.search);
  3797. if (sort === "up") {
  3798. if (params.toString() === "") {
  3799. location.href = `${location.href}?sort=${dom}Asc`;
  3800. } else if (!params.get("sort")) {
  3801. location.href = `${location.href}&sort=${dom}Asc`;
  3802. } else if (
  3803. params.get("sort") &&
  3804. params.get("sort").indexOf(`${dom}Asc`) !== -1
  3805. ) {
  3806. params.delete("sort");
  3807. location.search = params.toString();
  3808. } else {
  3809. params.set("sort", `${dom}Asc`);
  3810. let asc = params.toString();
  3811. location.search = asc;
  3812. }
  3813. } else if (sort === "down") {
  3814. if (params.toString() === "") {
  3815. location.href = `${location.href}?sort=${dom}Desc`;
  3816. } else if (!params.get("sort")) {
  3817. location.href = `${location.href}&sort=${dom}Desc`;
  3818. } else if (
  3819. params.get("sort") &&
  3820. params.get("sort").indexOf(`${dom}Desc`) !== -1
  3821. ) {
  3822. params.delete("sort");
  3823. location.search = params.toString();
  3824. } else {
  3825. params.set("sort", `${dom}Desc`);
  3826. let asc = params.toString();
  3827. location.search = asc;
  3828. }
  3829. }
  3830. },
  3831. setPrivate(uuid, privateFlag, index) {
  3832. const params = { _csrf: csrf, file: uuid, is_private: privateFlag };
  3833. this.$axios
  3834. .post("/attachments/private", this.qs.stringify(params))
  3835. .then((res) => {
  3836. this.$set(this.privates, index, privateFlag);
  3837. })
  3838. .catch((error) => {
  3839. console.log(error);
  3840. });
  3841. },
  3842. delDataset(uuid) {
  3843. let _this = this;
  3844. const params = { _csrf: csrf, file: uuid };
  3845. $("#data-dataset-delete-modal")
  3846. .modal({
  3847. closable: false,
  3848. onApprove() {
  3849. _this.$axios
  3850. .post("/attachments/delete", _this.qs.stringify(params))
  3851. .then((res) => {
  3852. // $('#'+uuid).hide()
  3853. location.reload();
  3854. })
  3855. .catch((error) => {
  3856. console.log(error);
  3857. });
  3858. },
  3859. })
  3860. .modal("show");
  3861. },
  3862. // getEditInit(){
  3863. // if($('#dataset-edit-value')){
  3864. // $this = $('#dataset-edit-value')
  3865. // this.ruleForm.title = $this.data('edit-title') || ''
  3866. // this.ruleForm.description = $this.data('edit-description') || ''
  3867. // this.ruleForm.category = $this.data('edit-category') || ''
  3868. // this.ruleForm.task = $this.data('edit-task') || ''
  3869. // this.ruleForm.license = $this.data('edit-license') || ''
  3870. // this.ruleForm.id = $this.data('edit-id')|| ''
  3871. // }
  3872. // },
  3873. editDataset(formName, id) {
  3874. let _this = this;
  3875. this.url = this.url.split(`/${id}`)[0];
  3876. this.$refs[formName].validate((valid) => {
  3877. if (valid) {
  3878. document.getElementById("mask").style.display = "block";
  3879. _this.$axios
  3880. .post(_this.url, _this.qs.stringify(_this.ruleForm1))
  3881. .then((res) => {
  3882. if (res.data.Code === 0) {
  3883. document.getElementById("mask").style.display = "none";
  3884. location.href = _this.url.split("/edit")[0] + "?type=-1";
  3885. } else {
  3886. console.log(res.data.Message);
  3887. }
  3888. document.getElementById("mask").style.display = "none";
  3889. })
  3890. .catch((err) => {
  3891. console.log(err);
  3892. });
  3893. } else {
  3894. return false;
  3895. }
  3896. });
  3897. },
  3898. editDatasetFile(id, backurl) {
  3899. let url = "/attachments/edit";
  3900. const params = { id: id, description: this.descfile, _csrf: csrf };
  3901. // document.getElementById("mask").style.display = "block"
  3902. this.$axios
  3903. .post(url, this.qs.stringify(params))
  3904. .then((res) => {
  3905. if (res.data.Code === 0) {
  3906. location.href = `${AppSubUrl}${backurl}/datasets`;
  3907. } else {
  3908. console.log(res.data.Message);
  3909. }
  3910. })
  3911. .catch((err) => {
  3912. console.log(err);
  3913. });
  3914. },
  3915. postStar(id, link) {
  3916. if (this.star_active) {
  3917. let url = link + "/" + id + "/unstar";
  3918. this.$axios.put(url).then((res) => {
  3919. if (res.data.Code === 0) {
  3920. this.star_active = false;
  3921. this.num_stars = this.num_stars - 1;
  3922. }
  3923. });
  3924. } else {
  3925. let url = link + "/" + id + "/star";
  3926. this.$axios.put(url).then((res) => {
  3927. if (res.data.Code === 0) {
  3928. this.star_active = true;
  3929. this.num_stars = this.num_stars + 1;
  3930. }
  3931. });
  3932. }
  3933. },
  3934. postSquareStar(id, link, index) {
  3935. if (this.starActives[index]) {
  3936. let url = link + "/" + id + "/unstar";
  3937. this.$axios.put(url).then((res) => {
  3938. if (res.data.Code === 0) {
  3939. this.$set(this.starActives, index, false);
  3940. this.$set(this.starItems, index, this.starItems[index] - 1);
  3941. }
  3942. });
  3943. } else {
  3944. let url = link + "/" + id + "/star";
  3945. this.$axios.put(url).then((res) => {
  3946. if (res.data.Code === 0) {
  3947. this.$set(this.starActives, index, true);
  3948. this.$set(this.starItems, index, this.starItems[index] + 1);
  3949. }
  3950. });
  3951. }
  3952. },
  3953. getTypeList() {
  3954. const params = new URLSearchParams(window.location.search);
  3955. if (window.location.search && params.has("type")) {
  3956. if (params.get("type") == 0) {
  3957. this.datasetType = "0";
  3958. }
  3959. if (params.get("type") == 1) {
  3960. this.datasetType = "1";
  3961. }
  3962. if (params.get("type") == -1) {
  3963. this.datasetType = "-1";
  3964. }
  3965. } else {
  3966. this.datasetType = "-1";
  3967. }
  3968. },
  3969. changeDatasetType(val) {
  3970. const searchParams = new URLSearchParams(window.location.search);
  3971. if (!window.location.search) {
  3972. window.location.href = window.location.href + "?type=" + val;
  3973. } else if (searchParams.has("type")) {
  3974. window.location.href = window.location.href.replace(
  3975. /type=([0-9]|-[0-9])/g,
  3976. "type=" + val
  3977. );
  3978. } else {
  3979. window.location.href = window.location.href + "&type=" + val;
  3980. }
  3981. },
  3982. gotoDatasetEidt(repolink, id) {
  3983. location.href = `${repolink}/datasets/attachments/edit/${id}`;
  3984. },
  3985. handleClick(repoLink, tabName, type) {
  3986. if (tabName == "first") {
  3987. this.page = 1;
  3988. this.searchDataItem = "";
  3989. this.getCurrentRepoDataset(repoLink, type);
  3990. }
  3991. if (tabName == "second") {
  3992. this.page = 1;
  3993. this.searchDataItem = "";
  3994. this.getMyDataset(repoLink, type);
  3995. }
  3996. if (tabName == "third") {
  3997. this.page = 1;
  3998. this.searchDataItem = "";
  3999. this.getPublicDataset(repoLink, type);
  4000. }
  4001. if (tabName == "fourth") {
  4002. this.page = 1;
  4003. this.searchDataItem = "";
  4004. this.getStarDataset(repoLink, type);
  4005. }
  4006. },
  4007. polling(checkStatuDataset, repoLink) {
  4008. this.timer = window.setInterval(() => {
  4009. setTimeout(() => {
  4010. this.getDatasetStatus(checkStatuDataset, repoLink);
  4011. }, 0);
  4012. }, 15000);
  4013. },
  4014. getDatasetStatus(checkStatuDataset, repoLink) {
  4015. const getmap = checkStatuDataset.map((item) => {
  4016. let url = `${AppSubUrl}${repolink}/datasets/status/${item.UUID}`;
  4017. return this.$axios.get(url);
  4018. });
  4019. this.$axios.all(getmap).then((res) => {
  4020. let flag = res.some((item) => {
  4021. return item.data.AttachmentStatus == 1;
  4022. });
  4023. flag && clearInterval(this.timer);
  4024. flag && this.refreshStatusDataset();
  4025. });
  4026. },
  4027. refreshStatusDataset() {
  4028. switch (this.activeName) {
  4029. case "first":
  4030. this.getCurrentRepoDataset(this.repolink, this.datasetType);
  4031. break;
  4032. case "second":
  4033. this.getMyDataset(this.repolink, this.datasetType);
  4034. break;
  4035. case "third":
  4036. this.getPublicDataset(this.repolink, this.datasetType);
  4037. break;
  4038. case "fourth":
  4039. this.getStarDataset(this.repolink, this.datasetType);
  4040. break;
  4041. }
  4042. },
  4043. getCurrentRepoDataset(repoLink, type) {
  4044. clearInterval(this.timer);
  4045. this.loadingDataIndex = true;
  4046. let url = repoLink + "/datasets/current_repo";
  4047. this.$axios
  4048. .get(url, {
  4049. params: {
  4050. type: type,
  4051. page: this.page,
  4052. q: this.searchDataItem,
  4053. },
  4054. })
  4055. .then((res) => {
  4056. if (res.data.result_code == "0") {
  4057. this.currentRepoDataset = JSON.parse(res.data.data);
  4058. const checkStatuDataset = this.currentRepoDataset.filter(
  4059. (item) => item.DecompressState === 2
  4060. );
  4061. if (checkStatuDataset.length > 0) {
  4062. this.polling(checkStatuDataset, repoLink);
  4063. }
  4064. this.totalnums = parseInt(res.data.count);
  4065. } else {
  4066. this.totalnums = 0;
  4067. }
  4068. this.loadingDataIndex = false;
  4069. });
  4070. },
  4071. getMyDataset(repoLink, type) {
  4072. clearInterval(this.timer);
  4073. this.loadingDataIndex = true;
  4074. let url = repoLink + "/datasets/my_datasets";
  4075. this.$axios
  4076. .get(url, {
  4077. params: {
  4078. type: type,
  4079. page: this.page,
  4080. q: this.searchDataItem,
  4081. },
  4082. })
  4083. .then((res) => {
  4084. this.myDataset = JSON.parse(res.data.data);
  4085. const checkStatuDataset = this.myDataset.filter(
  4086. (item) => item.DecompressState === 2
  4087. );
  4088. if (checkStatuDataset.length > 0) {
  4089. this.polling(checkStatuDataset, repoLink);
  4090. }
  4091. this.totalnums = parseInt(res.data.count);
  4092. this.loadingDataIndex = false;
  4093. });
  4094. },
  4095. getPublicDataset(repoLink, type) {
  4096. clearInterval(this.timer);
  4097. this.loadingDataIndex = true;
  4098. let url = repoLink + "/datasets/public_datasets";
  4099. this.$axios
  4100. .get(url, {
  4101. params: {
  4102. type: type,
  4103. page: this.page,
  4104. q: this.searchDataItem,
  4105. },
  4106. })
  4107. .then((res) => {
  4108. this.publicDataset = JSON.parse(res.data.data);
  4109. const checkStatuDataset = this.publicDataset.filter(
  4110. (item) => item.DecompressState === 2
  4111. );
  4112. if (checkStatuDataset.length > 0) {
  4113. this.polling(checkStatuDataset, repoLink);
  4114. }
  4115. this.totalnums = parseInt(res.data.count);
  4116. this.loadingDataIndex = false;
  4117. });
  4118. },
  4119. getStarDataset(repoLink, type) {
  4120. clearInterval(this.timer);
  4121. this.loadingDataIndex = true;
  4122. let url = repoLink + "/datasets/my_favorite";
  4123. this.$axios
  4124. .get(url, {
  4125. params: {
  4126. type: type,
  4127. page: this.page,
  4128. q: this.searchDataItem,
  4129. },
  4130. })
  4131. .then((res) => {
  4132. this.myFavoriteDataset = JSON.parse(res.data.data);
  4133. const checkStatuDataset = this.myFavoriteDataset.filter(
  4134. (item) => item.DecompressState === 2
  4135. );
  4136. if (checkStatuDataset.length > 0) {
  4137. this.polling(checkStatuDataset, repoLink);
  4138. }
  4139. this.totalnums = parseInt(res.data.count);
  4140. this.loadingDataIndex = false;
  4141. });
  4142. },
  4143. selectDataset(uuid, name) {
  4144. this.dataset_uuid = uuid;
  4145. this.dataset_name = name;
  4146. this.dialogVisible = false;
  4147. },
  4148. searchDataset() {
  4149. switch (this.activeName) {
  4150. case "first":
  4151. this.page = 1;
  4152. this.getCurrentRepoDataset(this.repolink, this.datasetType);
  4153. break;
  4154. case "second":
  4155. this.page = 1;
  4156. this.getMyDataset(this.repolink, this.datasetType);
  4157. break;
  4158. case "third":
  4159. this.page = 1;
  4160. this.getPublicDataset(this.repolink, this.datasetType);
  4161. break;
  4162. case "fourth":
  4163. this.page = 1;
  4164. this.getStarDataset(this.repolink, this.datasetType);
  4165. break;
  4166. }
  4167. },
  4168. },
  4169. watch: {
  4170. searchDataItem() {
  4171. switch (this.activeName) {
  4172. case "first":
  4173. this.page = 1;
  4174. this.getCurrentRepoDataset(this.repolink, this.datasetType);
  4175. break;
  4176. case "second":
  4177. this.page = 1;
  4178. this.getMyDataset(this.repolink, this.datasetType);
  4179. break;
  4180. case "third":
  4181. this.page = 1;
  4182. this.getPublicDataset(this.repolink, this.datasetType);
  4183. break;
  4184. case "fourth":
  4185. this.page = 1;
  4186. this.getStarDataset(this.repolink, this.datasetType);
  4187. break;
  4188. }
  4189. },
  4190. },
  4191. });
  4192. }
  4193. function initVueEditTopic() {
  4194. const el = document.getElementById("topic_edit1");
  4195. if (!el) {
  4196. return;
  4197. }
  4198. new Vue({
  4199. el: "#topic_edit1",
  4200. render: (h) => h(EditTopics),
  4201. });
  4202. }
  4203. function initVueContributors() {
  4204. const el = document.getElementById("Contributors");
  4205. if (!el) {
  4206. return;
  4207. }
  4208. new Vue({
  4209. el: "#Contributors",
  4210. render: (h) => h(Contributors),
  4211. });
  4212. }
  4213. // function initVueImages() {
  4214. // const el = document.getElementById('images');
  4215. // if (!el) {
  4216. // return;
  4217. // }
  4218. // new Vue({
  4219. // el: '#images',
  4220. // render: h => h(Images)
  4221. // });
  4222. // }
  4223. function initVueModel() {
  4224. const el = document.getElementById("model_list");
  4225. if (!el) {
  4226. return;
  4227. }
  4228. new Vue({
  4229. el: el,
  4230. render: (h) => h(Model),
  4231. });
  4232. }
  4233. function initVueDataAnalysis() {
  4234. const el = document.getElementById("data_analysis");
  4235. if (!el) {
  4236. return;
  4237. }
  4238. new Vue({
  4239. el: "#data_analysis",
  4240. router,
  4241. render: (h) => h(DataAnalysis),
  4242. });
  4243. }
  4244. function initVueWxAutorize() {
  4245. const el = document.getElementById("WxAutorize");
  4246. if (!el) {
  4247. return;
  4248. }
  4249. new Vue({
  4250. el: el,
  4251. render: (h) => h(WxAutorize),
  4252. });
  4253. }
  4254. function initVueselectDataset() {
  4255. const el = document.getElementById("select-multi-dataset");
  4256. if (!el) {
  4257. return;
  4258. }
  4259. new Vue({
  4260. el: el,
  4261. render: (h) => h(selectDataset),
  4262. });
  4263. }
  4264. function initVuereferenceDataset() {
  4265. const el = document.getElementById("reference-dataset");
  4266. if (!el) {
  4267. return;
  4268. }
  4269. new Vue({
  4270. el: el,
  4271. render: (h) => h(referenceDataset),
  4272. });
  4273. }
  4274. window.timeAddManual = function () {
  4275. $(".mini.modal")
  4276. .modal({
  4277. duration: 200,
  4278. onApprove() {
  4279. $("#add_time_manual_form").trigger("submit");
  4280. },
  4281. })
  4282. .modal("show");
  4283. };
  4284. window.toggleStopwatch = function () {
  4285. $("#toggle_stopwatch_form").trigger("submit");
  4286. };
  4287. window.cancelStopwatch = function () {
  4288. $("#cancel_stopwatch_form").trigger("submit");
  4289. };
  4290. function initFilterBranchTagDropdown(selector) {
  4291. $(selector).each(function () {
  4292. const $dropdown = $(this);
  4293. const $data = $dropdown.find(".data");
  4294. const data = {
  4295. items: [],
  4296. mode: $data.data("mode"),
  4297. searchTerm: "",
  4298. noResults: "",
  4299. canCreateBranch: false,
  4300. menuVisible: false,
  4301. active: 0,
  4302. };
  4303. $data.find(".item").each(function () {
  4304. data.items.push({
  4305. name: $(this).text(),
  4306. url: $(this).data("url"),
  4307. branch: $(this).hasClass("branch"),
  4308. tag: $(this).hasClass("tag"),
  4309. selected: $(this).hasClass("selected"),
  4310. });
  4311. });
  4312. $data.remove();
  4313. new Vue({
  4314. delimiters: ["${", "}"],
  4315. el: this,
  4316. data,
  4317. beforeMount() {
  4318. const vm = this;
  4319. this.noResults = vm.$el.getAttribute("data-no-results");
  4320. this.canCreateBranch =
  4321. vm.$el.getAttribute("data-can-create-branch") === "true";
  4322. document.body.addEventListener("click", (event) => {
  4323. if (vm.$el.contains(event.target)) {
  4324. return;
  4325. }
  4326. if (vm.menuVisible) {
  4327. Vue.set(vm, "menuVisible", false);
  4328. }
  4329. });
  4330. },
  4331. watch: {
  4332. menuVisible(visible) {
  4333. if (visible) {
  4334. this.focusSearchField();
  4335. }
  4336. },
  4337. },
  4338. computed: {
  4339. filteredItems() {
  4340. const vm = this;
  4341. const items = vm.items.filter((item) => {
  4342. return (
  4343. ((vm.mode === "branches" && item.branch) ||
  4344. (vm.mode === "tags" && item.tag)) &&
  4345. (!vm.searchTerm ||
  4346. item.name.toLowerCase().includes(vm.searchTerm.toLowerCase()))
  4347. );
  4348. });
  4349. vm.active = items.length === 0 && vm.showCreateNewBranch ? 0 : -1;
  4350. return items;
  4351. },
  4352. showNoResults() {
  4353. return this.filteredItems.length === 0 && !this.showCreateNewBranch;
  4354. },
  4355. showCreateNewBranch() {
  4356. const vm = this;
  4357. if (!this.canCreateBranch || !vm.searchTerm || vm.mode === "tags") {
  4358. return false;
  4359. }
  4360. return (
  4361. vm.items.filter(
  4362. (item) => item.name.toLowerCase() === vm.searchTerm.toLowerCase()
  4363. ).length === 0
  4364. );
  4365. },
  4366. },
  4367. methods: {
  4368. selectItem(item) {
  4369. const prev = this.getSelected();
  4370. if (prev !== null) {
  4371. prev.selected = false;
  4372. }
  4373. item.selected = true;
  4374. window.location.href = item.url;
  4375. },
  4376. createNewBranch() {
  4377. if (!this.showCreateNewBranch) {
  4378. return;
  4379. }
  4380. $(this.$refs.newBranchForm).trigger("submit");
  4381. },
  4382. focusSearchField() {
  4383. const vm = this;
  4384. Vue.nextTick(() => {
  4385. vm.$refs.searchField.focus();
  4386. });
  4387. },
  4388. getSelected() {
  4389. for (let i = 0, j = this.items.length; i < j; ++i) {
  4390. if (this.items[i].selected) return this.items[i];
  4391. }
  4392. return null;
  4393. },
  4394. getSelectedIndexInFiltered() {
  4395. for (let i = 0, j = this.filteredItems.length; i < j; ++i) {
  4396. if (this.filteredItems[i].selected) return i;
  4397. }
  4398. return -1;
  4399. },
  4400. scrollToActive() {
  4401. let el = this.$refs[`listItem${this.active}`];
  4402. if (!el || el.length === 0) {
  4403. return;
  4404. }
  4405. if (Array.isArray(el)) {
  4406. el = el[0];
  4407. }
  4408. const cont = this.$refs.scrollContainer;
  4409. if (el.offsetTop < cont.scrollTop) {
  4410. cont.scrollTop = el.offsetTop;
  4411. } else if (
  4412. el.offsetTop + el.clientHeight >
  4413. cont.scrollTop + cont.clientHeight
  4414. ) {
  4415. cont.scrollTop = el.offsetTop + el.clientHeight - cont.clientHeight;
  4416. }
  4417. },
  4418. keydown(event) {
  4419. const vm = this;
  4420. if (event.keyCode === 40) {
  4421. // arrow down
  4422. event.preventDefault();
  4423. if (vm.active === -1) {
  4424. vm.active = vm.getSelectedIndexInFiltered();
  4425. }
  4426. if (
  4427. vm.active + (vm.showCreateNewBranch ? 0 : 1) >=
  4428. vm.filteredItems.length
  4429. ) {
  4430. return;
  4431. }
  4432. vm.active++;
  4433. vm.scrollToActive();
  4434. }
  4435. if (event.keyCode === 38) {
  4436. // arrow up
  4437. event.preventDefault();
  4438. if (vm.active === -1) {
  4439. vm.active = vm.getSelectedIndexInFiltered();
  4440. }
  4441. if (vm.active <= 0) {
  4442. return;
  4443. }
  4444. vm.active--;
  4445. vm.scrollToActive();
  4446. }
  4447. if (event.keyCode === 13) {
  4448. // enter
  4449. event.preventDefault();
  4450. if (vm.active >= vm.filteredItems.length) {
  4451. vm.createNewBranch();
  4452. } else if (vm.active >= 0) {
  4453. vm.selectItem(vm.filteredItems[vm.active]);
  4454. }
  4455. }
  4456. if (event.keyCode === 27) {
  4457. // escape
  4458. event.preventDefault();
  4459. vm.menuVisible = false;
  4460. }
  4461. },
  4462. },
  4463. });
  4464. });
  4465. }
  4466. $(".commit-button").on("click", function (e) {
  4467. e.preventDefault();
  4468. $(this).parent().find(".commit-body").toggle();
  4469. });
  4470. function initNavbarContentToggle() {
  4471. const content = $("#navbar");
  4472. const toggle = $("#navbar-expand-toggle");
  4473. let isExpanded = false;
  4474. toggle.on("click", () => {
  4475. isExpanded = !isExpanded;
  4476. if (isExpanded) {
  4477. content.addClass("shown");
  4478. toggle.addClass("active");
  4479. } else {
  4480. content.removeClass("shown");
  4481. toggle.removeClass("active");
  4482. }
  4483. });
  4484. }
  4485. window.toggleDeadlineForm = function () {
  4486. $("#deadlineForm").fadeToggle(150);
  4487. };
  4488. window.setDeadline = function () {
  4489. const deadline = $("#deadlineDate").val();
  4490. window.updateDeadline(deadline);
  4491. };
  4492. window.updateDeadline = function (deadlineString) {
  4493. $("#deadline-err-invalid-date").hide();
  4494. $("#deadline-loader").addClass("loading");
  4495. let realDeadline = null;
  4496. if (deadlineString !== "") {
  4497. const newDate = Date.parse(deadlineString);
  4498. if (Number.isNaN(newDate)) {
  4499. $("#deadline-loader").removeClass("loading");
  4500. $("#deadline-err-invalid-date").show();
  4501. return false;
  4502. }
  4503. realDeadline = new Date(newDate);
  4504. }
  4505. $.ajax(`${$("#update-issue-deadline-form").attr("action")}/deadline`, {
  4506. data: JSON.stringify({
  4507. due_date: realDeadline,
  4508. }),
  4509. headers: {
  4510. "X-Csrf-Token": csrf,
  4511. "X-Remote": true,
  4512. },
  4513. contentType: "application/json",
  4514. type: "POST",
  4515. success() {
  4516. reload();
  4517. },
  4518. error() {
  4519. $("#deadline-loader").removeClass("loading");
  4520. $("#deadline-err-invalid-date").show();
  4521. },
  4522. });
  4523. };
  4524. window.deleteDependencyModal = function (id, type) {
  4525. $(".remove-dependency")
  4526. .modal({
  4527. closable: false,
  4528. duration: 200,
  4529. onApprove() {
  4530. $("#removeDependencyID").val(id);
  4531. $("#dependencyType").val(type);
  4532. $("#removeDependencyForm").trigger("submit");
  4533. },
  4534. })
  4535. .modal("show");
  4536. };
  4537. function initIssueList() {
  4538. const repolink = $("#repolink").val();
  4539. const repoId = $("#repoId").val();
  4540. const crossRepoSearch = $("#crossRepoSearch").val();
  4541. const tp = $("#type").val();
  4542. let issueSearchUrl = `${AppSubUrl}/api/v1/repos/${repolink}/issues?q={query}&type=${tp}`;
  4543. if (crossRepoSearch === "true") {
  4544. issueSearchUrl = `${AppSubUrl}/api/v1/repos/issues/search?q={query}&priority_repo_id=${repoId}&type=${tp}`;
  4545. }
  4546. $("#new-dependency-drop-list").dropdown({
  4547. apiSettings: {
  4548. url: issueSearchUrl,
  4549. onResponse(response) {
  4550. const filteredResponse = { success: true, results: [] };
  4551. const currIssueId = $("#new-dependency-drop-list").data("issue-id");
  4552. // Parse the response from the api to work with our dropdown
  4553. $.each(response, (_i, issue) => {
  4554. // Don't list current issue in the dependency list.
  4555. if (issue.id === currIssueId) {
  4556. return;
  4557. }
  4558. filteredResponse.results.push({
  4559. name: `#${issue.number} ${htmlEncode(
  4560. issue.title
  4561. )}<div class="text small dont-break-out">${htmlEncode(
  4562. issue.repository.full_name
  4563. )}</div>`,
  4564. value: issue.id,
  4565. });
  4566. });
  4567. return filteredResponse;
  4568. },
  4569. cache: false,
  4570. },
  4571. fullTextSearch: true,
  4572. });
  4573. $(".menu a.label-filter-item").each(function () {
  4574. $(this).on("click", function (e) {
  4575. if (e.altKey) {
  4576. e.preventDefault();
  4577. const href = $(this).attr("href");
  4578. const id = $(this).data("label-id");
  4579. const regStr = `labels=(-?[0-9]+%2c)*(${id})(%2c-?[0-9]+)*&`;
  4580. const newStr = "labels=$1-$2$3&";
  4581. window.location = href.replace(new RegExp(regStr), newStr);
  4582. }
  4583. });
  4584. });
  4585. $(".menu .ui.dropdown.label-filter").on("keydown", (e) => {
  4586. if (e.altKey && e.keyCode === 13) {
  4587. const selectedItems = $(
  4588. ".menu .ui.dropdown.label-filter .menu .item.selected"
  4589. );
  4590. if (selectedItems.length > 0) {
  4591. const item = $(selectedItems[0]);
  4592. const href = item.attr("href");
  4593. const id = item.data("label-id");
  4594. const regStr = `labels=(-?[0-9]+%2c)*(${id})(%2c-?[0-9]+)*&`;
  4595. const newStr = "labels=$1-$2$3&";
  4596. window.location = href.replace(new RegExp(regStr), newStr);
  4597. }
  4598. }
  4599. });
  4600. }
  4601. window.cancelCodeComment = function (btn) {
  4602. const form = $(btn).closest("form");
  4603. if (form.length > 0 && form.hasClass("comment-form")) {
  4604. form.addClass("hide");
  4605. form.parent().find("button.comment-form-reply").show();
  4606. } else {
  4607. form.closest(".comment-code-cloud").remove();
  4608. }
  4609. };
  4610. window.submitReply = function (btn) {
  4611. const form = $(btn).closest("form");
  4612. if (form.length > 0 && form.hasClass("comment-form")) {
  4613. form.trigger("submit");
  4614. }
  4615. };
  4616. window.onOAuthLoginClick = function () {
  4617. const oauthLoader = $("#oauth2-login-loader");
  4618. const oauthNav = $("#oauth2-login-navigator");
  4619. oauthNav.hide();
  4620. oauthLoader.removeClass("disabled");
  4621. setTimeout(() => {
  4622. // recover previous content to let user try again
  4623. // usually redirection will be performed before this action
  4624. oauthLoader.addClass("disabled");
  4625. oauthNav.show();
  4626. }, 5000);
  4627. };
  4628. // Pull SVGs via AJAX to workaround CORS issues with <use> tags
  4629. // https://css-tricks.com/ajaxing-svg-sprite/
  4630. $.get(`${window.config.StaticUrlPrefix}/img/svg/icons.svg`, (data) => {
  4631. const div = document.createElement("div");
  4632. div.style.display = "none";
  4633. div.innerHTML = new XMLSerializer().serializeToString(data.documentElement);
  4634. document.body.insertBefore(div, document.body.childNodes[0]);
  4635. });
  4636. function initDropDown() {
  4637. $("#dropdown_PageHome").dropdown({
  4638. on: "hover", //鼠标悬浮显示,默认值是click
  4639. });
  4640. $("#dropdown_explore").dropdown({
  4641. on: "hover", //鼠标悬浮显示,默认值是click
  4642. });
  4643. }
  4644. //云脑提示
  4645. $(".question.circle.icon.cloudbrain-question").hover(function () {
  4646. $(this).popup("show");
  4647. $(".ui.popup.mini.top.center").css({
  4648. "border-color": "rgba(50, 145, 248, 100)",
  4649. color: "rgba(3, 102, 214, 100)",
  4650. "border-radius": "5px",
  4651. "border-shadow": "none",
  4652. });
  4653. });
  4654. //云脑详情页面跳转回上一个页面
  4655. // $(".section.backTodeBug").attr("href",localStorage.getItem('all'))
  4656. //新建调试取消跳转
  4657. // $(".ui.button.cancel").attr("href",localStorage.getItem('all'))
  4658. function initcreateRepo() {
  4659. let timeout;
  4660. let keydown_flag = false;
  4661. const urlAdd =
  4662. location.href.split("/")[0] + "//" + location.href.split("/")[2];
  4663. let owner = $("#ownerDropdown div.text").attr("title");
  4664. $(document).ready(function () {
  4665. $("#ownerDropdown").dropdown({
  4666. onChange: function (value, text, $choice) {
  4667. owner = $choice[0].getAttribute("title");
  4668. $("#repoAdress").css("display", "flex");
  4669. $("#repoAdress span").text(
  4670. urlAdd + "/" + owner + "/" + $("#repo_name").val() + ".git"
  4671. );
  4672. },
  4673. });
  4674. });
  4675. $("#repo_name").keyup(function () {
  4676. keydown_flag = $("#repo_name").val() ? true : false;
  4677. if (keydown_flag) {
  4678. $("#repoAdress").css("display", "flex");
  4679. $("#repoAdress span").text(
  4680. urlAdd + "/" + owner + "/" + $("#repo_name").val() + ".git"
  4681. );
  4682. } else {
  4683. $("#repoAdress").css("display", "none");
  4684. $("#repo_name").attr("placeholder", "");
  4685. }
  4686. });
  4687. $("#create_repo_form").form({
  4688. on: "blur",
  4689. // inline:true,
  4690. fields: {
  4691. alias: {
  4692. identifier: "alias",
  4693. rules: [
  4694. {
  4695. type: "regExp[/^[\u4E00-\u9FA5A-Za-z0-9_.-]{1,100}$/]",
  4696. },
  4697. ],
  4698. },
  4699. repo_name: {
  4700. identifier: "repo_name",
  4701. rules: [
  4702. {
  4703. type: "regExp[/^[A-Za-z0-9_.-]{1,100}$/]",
  4704. },
  4705. ],
  4706. },
  4707. },
  4708. onFailure: function (e) {
  4709. return false;
  4710. },
  4711. });
  4712. $("#alias").bind("input propertychange", function (event) {
  4713. clearTimeout(timeout);
  4714. timeout = setTimeout(() => {
  4715. //在此处写调用的方法,可以实现仅最后一次操作生效
  4716. const aliasValue = $("#alias").val();
  4717. if (keydown_flag) {
  4718. $("#repo_name").attr("placeholder", "");
  4719. } else if (aliasValue) {
  4720. $("#repo_name").attr("placeholder", "正在获取路径...");
  4721. $.get(
  4722. `${window.config.AppSubUrl}/repo/check_name?q=${aliasValue}&owner=${owner}`,
  4723. (data) => {
  4724. const repo_name = data.name;
  4725. $("#repo_name").val(repo_name);
  4726. repo_name && $("#repo_name").parent().removeClass("error");
  4727. $("#repoAdress").css("display", "flex");
  4728. $("#repoAdress span").text(
  4729. urlAdd + "/" + owner + "/" + $("#repo_name").val() + ".git"
  4730. );
  4731. $("#repo_name").attr("placeholder", "");
  4732. }
  4733. );
  4734. } else {
  4735. $("#repo_name").val("");
  4736. $("#repo_name").attr("placeholder", "");
  4737. $("#repoAdress").css("display", "none");
  4738. }
  4739. }, 500);
  4740. });
  4741. }
  4742. initcreateRepo();
  4743. function initChartsNpu() {
  4744. const repoPath = $('.metric_chart').data('path')
  4745. let options = {
  4746. legend: {
  4747. data: [],
  4748. },
  4749. grid: {
  4750. top: "35%",
  4751. bottom: "2%",
  4752. x: "2%",
  4753. containLabel: true,
  4754. },
  4755. tooltip: {
  4756. trigger: "axis",
  4757. backgroundColor: "rgb(51, 56, 84)",
  4758. borderColor: "rgb(51, 51, 51)",
  4759. borderWidth: 0,
  4760. textStyle: {
  4761. color: "#fff",
  4762. },
  4763. axisPointer: {
  4764. type: "line",
  4765. },
  4766. },
  4767. xAxis: {
  4768. type: "category",
  4769. data: [],
  4770. boundaryGap: false,
  4771. axisLabel: {
  4772. interval: "auto",
  4773. },
  4774. name: "",
  4775. },
  4776. yAxis: {
  4777. show: true,
  4778. name: "占有率(%)",
  4779. axisLine: {
  4780. show: true,
  4781. },
  4782. axisTick: { show: true },
  4783. },
  4784. series: [],
  4785. };
  4786. const sortBy = (arr, k) =>
  4787. arr.concat().sort((a, b) => (a[k] > b[k] ? 1 : a[k] < b[k] ? -1 : 0));
  4788. $(".metric_chart").click(function (e) {
  4789. let versionName = $(this).data("version");
  4790. let myCharts = echarts.init(
  4791. document.getElementById(`metric-${versionName}`)
  4792. );
  4793. $.get(
  4794. `${window.config.AppSubUrl}/api/v1/repos/${repoPath}`,
  4795. (res) => {
  4796. let filterDta = res.MetricsInfo.filter((item) => {
  4797. return ![
  4798. "recvBytesRate",
  4799. "diskWriteRate",
  4800. "sendBytesRate",
  4801. "diskReadRate",
  4802. ].includes(item.metric);
  4803. });
  4804. filterDta = sortBy(filterDta, "metric");
  4805. let legenData = filterDta.map((item) => {
  4806. return item.metric;
  4807. });
  4808. let seriesData = filterDta.map((item) => {
  4809. let value = item.value.map((item) => {
  4810. return item > 0 ? item : "0";
  4811. });
  4812. let seriesOption = {
  4813. name: item.metric,
  4814. type: "line",
  4815. symbol: "circle",
  4816. symbolSize: 10,
  4817. smooth: true,
  4818. showSymbol: false,
  4819. lineStyle: {
  4820. width: 2,
  4821. shadowColor: "rgba(0,0,0,0.3)",
  4822. shadowBlur: 10,
  4823. shadowOffsetY: 8,
  4824. },
  4825. data: value,
  4826. };
  4827. return seriesOption;
  4828. });
  4829. let xAxisValue = res.Interval === 1 ? "时间(min)" : "时间(hour)";
  4830. let xLength = res.MetricsInfo[0].value.length;
  4831. options.xAxis.name = xAxisValue;
  4832. options.xAxis.data = Array.from(
  4833. { length: xLength },
  4834. (_, index) => index
  4835. );
  4836. options.legend.data = legenData;
  4837. options.series = seriesData;
  4838. options && myCharts.setOption(options);
  4839. }
  4840. );
  4841. options && myCharts.setOption(options);
  4842. });
  4843. }
  4844. initChartsNpu();
  4845. Fancybox.bind('.gallery img', {
  4846. // Do not create a gallery
  4847. groupAttr: null,
  4848. // Do not hide page scrollbars
  4849. hideScrollbar: false,
  4850. // Disable drag to close guesture
  4851. dragToClose: false,
  4852. // Hide close button
  4853. closeButton: false,
  4854. // Disable toolbar
  4855. Toolbar: false,
  4856. // Disable zoom animation; close on click and wheel events
  4857. Image: {
  4858. zoom: false,
  4859. click: "close",
  4860. wheel: "close",
  4861. },
  4862. // Custom animations
  4863. showClass: "fancybox-zoomIn",
  4864. hideClass: "fancybox-zoomOut",
  4865. });
  4866. function initTopToHome() {
  4867. const topToHomeEl = $('.__go-top');
  4868. $(window).scroll(function (e) {
  4869. const scrollTop = $(document).scrollTop();
  4870. const winHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
  4871. if (scrollTop > winHeight * 0.5) {
  4872. topToHomeEl.fadeIn();
  4873. } else {
  4874. topToHomeEl.fadeOut();
  4875. }
  4876. });
  4877. topToHomeEl.on('click', function() {
  4878. $('html,body').animate({ scrollTop: 0 }, 'slow', 'swing');
  4879. });
  4880. }
  4881. initTopToHome();
  4882. $(".question.circle.icon").hover(function () {
  4883. $(this).popup("show");
  4884. });