├── .discourse-compatibility ├── .github └── workflows │ └── discourse-plugin.yml ├── .gitignore ├── .npmrc ├── .prettierrc.cjs ├── .rubocop.yml ├── .streerc ├── .template-lintrc.cjs ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── app ├── jobs │ ├── regular │ │ ├── create_github_linkback.rb │ │ └── replace_github_non_permalinks.rb │ └── scheduled │ │ └── grant_github_badges.rb ├── lib │ ├── commits_populator.rb │ ├── github_badges.rb │ ├── github_badges_repo_setting_validator.rb │ ├── github_linkback.rb │ ├── github_linkback_access_token_setting_validator.rb │ └── github_permalinks.rb └── models │ ├── github_commit.rb │ └── github_repo.rb ├── config ├── locales │ ├── client.ar.yml │ ├── client.be.yml │ ├── client.bg.yml │ ├── client.bs_BA.yml │ ├── client.ca.yml │ ├── client.cs.yml │ ├── client.da.yml │ ├── client.de.yml │ ├── client.el.yml │ ├── client.en.yml │ ├── client.en_GB.yml │ ├── client.es.yml │ ├── client.et.yml │ ├── client.fa_IR.yml │ ├── client.fi.yml │ ├── client.fr.yml │ ├── client.gl.yml │ ├── client.he.yml │ ├── client.hr.yml │ ├── client.hu.yml │ ├── client.hy.yml │ ├── client.id.yml │ ├── client.it.yml │ ├── client.ja.yml │ ├── client.ko.yml │ ├── client.lt.yml │ ├── client.lv.yml │ ├── client.nb_NO.yml │ ├── client.nl.yml │ ├── client.pl_PL.yml │ ├── client.pt.yml │ ├── client.pt_BR.yml │ ├── client.ro.yml │ ├── client.ru.yml │ ├── client.sk.yml │ ├── client.sl.yml │ ├── client.sq.yml │ ├── client.sr.yml │ ├── client.sv.yml │ ├── client.sw.yml │ ├── client.te.yml │ ├── client.th.yml │ ├── client.tr_TR.yml │ ├── client.ug.yml │ ├── client.uk.yml │ ├── client.ur.yml │ ├── client.vi.yml │ ├── client.zh_CN.yml │ ├── client.zh_TW.yml │ ├── server.ar.yml │ ├── server.be.yml │ ├── server.bg.yml │ ├── server.bs_BA.yml │ ├── server.ca.yml │ ├── server.cs.yml │ ├── server.da.yml │ ├── server.de.yml │ ├── server.el.yml │ ├── server.en.yml │ ├── server.en_GB.yml │ ├── server.es.yml │ ├── server.et.yml │ ├── server.fa_IR.yml │ ├── server.fi.yml │ ├── server.fr.yml │ ├── server.gl.yml │ ├── server.he.yml │ ├── server.hr.yml │ ├── server.hu.yml │ ├── server.hy.yml │ ├── server.id.yml │ ├── server.it.yml │ ├── server.ja.yml │ ├── server.ko.yml │ ├── server.lt.yml │ ├── server.lv.yml │ ├── server.nb_NO.yml │ ├── server.nl.yml │ ├── server.pl_PL.yml │ ├── server.pt.yml │ ├── server.pt_BR.yml │ ├── server.ro.yml │ ├── server.ru.yml │ ├── server.sk.yml │ ├── server.sl.yml │ ├── server.sq.yml │ ├── server.sr.yml │ ├── server.sv.yml │ ├── server.sw.yml │ ├── server.te.yml │ ├── server.th.yml │ ├── server.tr_TR.yml │ ├── server.ug.yml │ ├── server.uk.yml │ ├── server.ur.yml │ ├── server.vi.yml │ ├── server.zh_CN.yml │ └── server.zh_TW.yml └── settings.yml ├── db └── migrate │ ├── 20190617035051_rename_site_setting_github_badges_repo.rb │ ├── 20190618174229_create_github_repos.rb │ ├── 20190618183340_create_github_commits.rb │ ├── 20201210032852_discourse_github_rebuild_git_history.rb │ └── 20230227050148_update_github_badge_icons.rb ├── eslint.config.mjs ├── package.json ├── plugin.rb ├── pnpm-lock.yaml ├── spec ├── jobs │ ├── create_github_linkback_spec.rb │ └── replace_github_non_permalinks_spec.rb ├── lib │ ├── commits_populator_spec.rb │ ├── github_badges_repo_setting_validator_spec.rb │ ├── github_badges_spec.rb │ ├── github_linkback_access_token_setting_validator_spec.rb │ ├── github_linkback_spec.rb │ └── github_permalinks_spec.rb ├── models │ └── github_repo_spec.rb └── system │ └── core_features_spec.rb ├── stylelint.config.mjs └── translator.yml /.discourse-compatibility: -------------------------------------------------------------------------------- 1 | < 3.5.0.beta1-dev: 59e5fc5692959c6c564ab0e09de364ccfedd6702 2 | < 3.4.0.beta1-dev: e24de3f5cd6ec5cef17dc3e07dfb3bfac8867e08 3 | < 3.3.0.beta1-dev: 53e22ccbd32dd868435f66efa2d48e0389673dea 4 | 3.1.999: 8aa068d56ef010cecaabd50657e7753f4bbecc1f 5 | -------------------------------------------------------------------------------- /.github/workflows/discourse-plugin.yml: -------------------------------------------------------------------------------- 1 | name: Discourse Plugin 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | ci: 11 | uses: discourse/.github/.github/workflows/discourse-plugin.yml@v1 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /gems 2 | node_modules 3 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict = true 2 | auto-install-peers = false 3 | -------------------------------------------------------------------------------- /.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = require("@discourse/lint-configs/prettier"); 2 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | inherit_gem: 2 | rubocop-discourse: stree-compat.yml 3 | AllCops: 4 | Exclude: 5 | - "gems/**/*" 6 | - "vendor/bundle/**/*" 7 | -------------------------------------------------------------------------------- /.streerc: -------------------------------------------------------------------------------- 1 | --print-width=100 2 | --plugins=plugin/trailing_comma 3 | -------------------------------------------------------------------------------- /.template-lintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = require("@discourse/lint-configs/template-lint"); 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | group :development do 6 | gem "rubocop-discourse" 7 | gem "syntax_tree" 8 | end 9 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | activesupport (8.0.2) 5 | base64 6 | benchmark (>= 0.3) 7 | bigdecimal 8 | concurrent-ruby (~> 1.0, >= 1.3.1) 9 | connection_pool (>= 2.2.5) 10 | drb 11 | i18n (>= 1.6, < 2) 12 | logger (>= 1.4.2) 13 | minitest (>= 5.1) 14 | securerandom (>= 0.3) 15 | tzinfo (~> 2.0, >= 2.0.5) 16 | uri (>= 0.13.1) 17 | ast (2.4.3) 18 | base64 (0.2.0) 19 | benchmark (0.4.0) 20 | bigdecimal (3.1.9) 21 | concurrent-ruby (1.3.5) 22 | connection_pool (2.5.3) 23 | drb (2.2.3) 24 | i18n (1.14.7) 25 | concurrent-ruby (~> 1.0) 26 | json (2.12.2) 27 | language_server-protocol (3.17.0.5) 28 | lint_roller (1.1.0) 29 | logger (1.7.0) 30 | minitest (5.25.5) 31 | parallel (1.27.0) 32 | parser (3.3.8.0) 33 | ast (~> 2.4.1) 34 | racc 35 | prettier_print (1.2.1) 36 | prism (1.4.0) 37 | racc (1.8.1) 38 | rack (3.1.15) 39 | rainbow (3.1.1) 40 | regexp_parser (2.10.0) 41 | rubocop (1.75.8) 42 | json (~> 2.3) 43 | language_server-protocol (~> 3.17.0.2) 44 | lint_roller (~> 1.1.0) 45 | parallel (~> 1.10) 46 | parser (>= 3.3.0.2) 47 | rainbow (>= 2.2.2, < 4.0) 48 | regexp_parser (>= 2.9.3, < 3.0) 49 | rubocop-ast (>= 1.44.0, < 2.0) 50 | ruby-progressbar (~> 1.7) 51 | unicode-display_width (>= 2.4.0, < 4.0) 52 | rubocop-ast (1.44.1) 53 | parser (>= 3.3.7.2) 54 | prism (~> 1.4) 55 | rubocop-capybara (2.22.1) 56 | lint_roller (~> 1.1) 57 | rubocop (~> 1.72, >= 1.72.1) 58 | rubocop-discourse (3.12.1) 59 | activesupport (>= 6.1) 60 | lint_roller (>= 1.1.0) 61 | rubocop (>= 1.73.2) 62 | rubocop-capybara (>= 2.22.0) 63 | rubocop-factory_bot (>= 2.27.0) 64 | rubocop-rails (>= 2.30.3) 65 | rubocop-rspec (>= 3.0.1) 66 | rubocop-rspec_rails (>= 2.31.0) 67 | rubocop-factory_bot (2.27.1) 68 | lint_roller (~> 1.1) 69 | rubocop (~> 1.72, >= 1.72.1) 70 | rubocop-rails (2.32.0) 71 | activesupport (>= 4.2.0) 72 | lint_roller (~> 1.1) 73 | rack (>= 1.1) 74 | rubocop (>= 1.75.0, < 2.0) 75 | rubocop-ast (>= 1.44.0, < 2.0) 76 | rubocop-rspec (3.6.0) 77 | lint_roller (~> 1.1) 78 | rubocop (~> 1.72, >= 1.72.1) 79 | rubocop-rspec_rails (2.31.0) 80 | lint_roller (~> 1.1) 81 | rubocop (~> 1.72, >= 1.72.1) 82 | rubocop-rspec (~> 3.5) 83 | ruby-progressbar (1.13.0) 84 | securerandom (0.4.1) 85 | syntax_tree (6.2.0) 86 | prettier_print (>= 1.2.0) 87 | tzinfo (2.0.6) 88 | concurrent-ruby (~> 1.0) 89 | unicode-display_width (3.1.4) 90 | unicode-emoji (~> 4.0, >= 4.0.4) 91 | unicode-emoji (4.0.4) 92 | uri (1.0.3) 93 | 94 | PLATFORMS 95 | ruby 96 | 97 | DEPENDENCIES 98 | rubocop-discourse 99 | syntax_tree 100 | 101 | BUNDLED WITH 102 | 2.6.9 103 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Civilized Discourse Construction Kit, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Discourse Github 2 | 3 | https://meta.discourse.org/t/discourse-github/99895/ 4 | 5 | This plugin combines functionality of deprecated github_badges and discourse-github-linkback plugins. 6 | 7 | ### Installation 8 | 9 | Follow the [plugin installation guide](https://meta.discourse.org/t/install-a-plugin/19157). 10 | 11 | ### Github Badges 12 | 13 | Assign badges to your users based on GitHub contributions. 14 | 15 | #### How to use: 16 | 17 | 1. Enable `github badges enabled` in Settings -> Plugins. 18 | 19 | 2. Generate an access token on Github. Be sure to give it only the `public_repo` scope. Paste that token into the `github linkback access token` setting. 20 | 21 | 3. Add URL of the GitHub repo to scan for contributions to the `github badges repo` site setting. 22 | 23 | ### Github Linkback 24 | 25 | Create a link from a Github pull request or commit back to a Discourse post where it is mentioned. 26 | 27 | #### How to use: 28 | 29 | 1. Enable `github linkback enabled` in Settings -> Plugins. 30 | 31 | 2. Generate an [access token](https://github.com/settings/tokens) on Github. 32 | Be sure to give it only the `public_repo` scope. Paste that token into the 33 | `github linkback access token` setting. 34 | 35 | 3. Finally, add the projects you wish to post to in the `github linkback projects` site setting in the formats: 36 | - `username/repository` for specific repositories 37 | - `username/*` for all repositories of a certain user 38 | 39 | ### Github Permalink 40 | 41 | Replace Github non-permalinks with [permalinks](https://help.github.com/articles/getting-permanent-links-to-files/). 42 | 43 | #### How to use: 44 | 45 | 1. Enable `github permalinks enabled` in Settings -> Plugins. 46 | 2. If you want to exclude certain files or directories from permalink overwrites, you can modify that in the `github permalinks exclude` site setting in the following formats: 47 | - `filename` links to all files with matching filename 48 | - `username/*` links to all repositories belonging to the user/organization 49 | - `username/repository/*` links to any file in the repository 50 | - `username/repository/directory/*` links to any file in the directory within repository 51 | - `username/repository/file.rb` a specific file 52 | -------------------------------------------------------------------------------- /app/jobs/regular/create_github_linkback.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Jobs 4 | class CreateGithubLinkback < ::Jobs::Base 5 | def execute(args) 6 | return unless SiteSetting.enable_discourse_github_plugin? 7 | return unless SiteSetting.github_linkback_enabled? 8 | return if (post = Post.find_by_id(args[:post_id])).blank? 9 | GithubLinkback.new(post).create 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/jobs/regular/replace_github_non_permalinks.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "net/http" 4 | require "json" 5 | require "uri" 6 | 7 | module Jobs 8 | class ReplaceGithubNonPermalinks < ::Jobs::Base 9 | sidekiq_options queue: "low" 10 | 11 | def execute(args) 12 | return unless SiteSetting.enable_discourse_github_plugin? 13 | return unless SiteSetting.github_permalinks_enabled? 14 | 15 | post_id = args[:post_id] 16 | raise Discourse::InvalidParameters.new(:post_id) if post_id.blank? 17 | 18 | post = Post.find_by(id: post_id) 19 | return if post.blank? 20 | 21 | raw = post.raw.dup 22 | start_raw = raw.dup 23 | 24 | regex = 25 | %r{github\.com/(?[^/]+)/(?[^/\s]+)/blob/(?[^/\s]+)/(?[^#\s]+)(?#(L([^-\s]*)(-L(\d*))?))?}i 26 | 27 | matches = post.raw.scan(regex) 28 | matches.each do |user, repo, sha1, file, from_to| 29 | next if excluded?(user, repo, file) 30 | 31 | begin 32 | api_url = "https://api.github.com/repos/#{user}/#{repo}/commits/#{sha1}" 33 | json = api_request(api_url) 34 | if json && (json["sha"] != sha1) 35 | new_sha = json["sha"] 36 | old_url = "github.com/#{user}/#{repo}/blob/#{sha1}/#{file}#{from_to}" 37 | new_url = "github.com/#{user}/#{repo}/blob/#{new_sha}/#{file}#{from_to}" 38 | raw.sub!(old_url, new_url) 39 | end 40 | rescue => e 41 | log( 42 | :error, 43 | "Failed to replace Github link with permalink in post #{post_id}\n" + e.message + "\n" + 44 | e.backtrace.join("\n"), 45 | ) 46 | end 47 | end 48 | 49 | post.reload 50 | 51 | if start_raw == post.raw && raw != post.raw 52 | changes = { raw: raw, edit_reason: I18n.t("replace_github_link.edit_reason") } 53 | post.revise(Discourse.system_user, changes, bypass_bump: true) 54 | end 55 | end 56 | 57 | def excluded?(user, repo, file) 58 | excluded = SiteSetting.github_permalinks_exclude.split("|") 59 | 60 | excluded.each do |e| 61 | path_parts = e.split("/") 62 | # when only filename is provided 63 | if path_parts.length == 1 64 | return true if file == e 65 | next 66 | end 67 | 68 | path_parts.each { |p| p.sub!("*", "\\S+") } 69 | 70 | regex = Regexp.new(path_parts.join("\/")) 71 | return true if "#{user}/#{repo}/#{file}".match(regex) 72 | end 73 | 74 | false 75 | end 76 | 77 | private 78 | 79 | def api_request(url) 80 | uri = URI(url) 81 | response = Net::HTTP.get_response(uri) 82 | 83 | JSON.parse(response.body) if response.kind_of? Net::HTTPSuccess 84 | end 85 | 86 | def log(log_level, message) 87 | Rails.logger.public_send( 88 | log_level, 89 | "#{RailsMultisite::ConnectionManagement.current_db}: #{message}", 90 | ) 91 | end 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /app/jobs/scheduled/grant_github_badges.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiscourseGithubPlugin 4 | class UpdateJob < ::Jobs::Scheduled 5 | every 4.hours 6 | 7 | def execute(args) 8 | return unless SiteSetting.enable_discourse_github_plugin? 9 | return unless SiteSetting.github_badges_enabled? 10 | return if SiteSetting.github_linkback_access_token.blank? 11 | 12 | GithubRepo.repos.each { |repo| CommitsPopulator.new(repo).populate! } 13 | GithubBadges.grant! 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/lib/commits_populator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiscourseGithubPlugin 4 | class CommitsPopulator 5 | MERGE_COMMIT_REGEX = /^Merge pull request/ 6 | HISTORY_COMPLETE = "history-complete" 7 | class GraphQLError < StandardError 8 | end 9 | 10 | ROLES = { committer: 0, contributor: 1 } 11 | 12 | class PaginatedCommits 13 | def initialize(octokit, repo, cursor: nil, page_size: 100) 14 | @client = octokit 15 | @repo = repo 16 | @cursor = cursor 17 | @page_size = page_size 18 | raise ArgumentError, "page_size arg must be <= 100" if page_size > 100 19 | if cursor && !cursor.match?(/^\h{40}\s(\d+)$/) 20 | raise ArgumentError, 21 | "cursor must be a 40-characters hex string followed by a space and a number" 22 | end 23 | fetch_commits 24 | end 25 | 26 | def next 27 | cursor = next_cursor 28 | return unless cursor 29 | PaginatedCommits.new(@client, @repo, cursor: cursor, page_size: @page_size) 30 | end 31 | 32 | def commits 33 | @data.repository.defaultBranchRef.target.history.nodes 34 | end 35 | 36 | def next_cursor 37 | info = @data.repository.defaultBranchRef.target.history.pageInfo 38 | return unless info.hasNextPage 39 | info.endCursor 40 | end 41 | 42 | private 43 | 44 | def fetch_commits 45 | owner, name = @repo.name.split("/", 2) 46 | history_args = "first: #{@page_size}" 47 | history_args += ", after: #{@cursor.inspect}" if @cursor 48 | 49 | query = <<~QUERY 50 | query { 51 | repository(name: #{name.inspect}, owner: #{owner.inspect}) { 52 | defaultBranchRef { 53 | target { 54 | ... on Commit { 55 | history(#{history_args}) { 56 | pageInfo { 57 | endCursor 58 | hasNextPage 59 | } 60 | nodes { 61 | oid 62 | message 63 | committedDate 64 | associatedPullRequests(first: 1) { 65 | nodes { 66 | author { 67 | login 68 | } 69 | mergedBy { 70 | login 71 | } 72 | } 73 | } 74 | author { 75 | email 76 | } 77 | } 78 | } 79 | } 80 | } 81 | } 82 | } 83 | } 84 | QUERY 85 | response = @client.post("/graphql", { query: query }.to_json) 86 | raise GraphQLError, response.errors.inspect if response.errors 87 | raise GraphQLError, response.message if !response.data 88 | @data = response.data 89 | end 90 | end 91 | 92 | def initialize(repo) 93 | @repo = repo 94 | @client = 95 | Octokit::Client.new(access_token: SiteSetting.github_linkback_access_token, per_page: 100) 96 | end 97 | 98 | def populate! 99 | return unless SiteSetting.github_badges_enabled? 100 | return if @client.branches(@repo.name).empty? 101 | 102 | if @repo.commits.size == 0 103 | build_history! 104 | else 105 | front_sha = Discourse.redis.get(front_commit_redis_key) 106 | if front_sha.present? && removed?(front_sha) 107 | # there has been a force push, next run will rebuild history 108 | @repo.commits.delete_all 109 | Discourse.redis.del(back_cursor_redis_key) 110 | Discourse.redis.del(front_commit_redis_key) 111 | return 112 | end 113 | fetch_new_commits!(front_sha) 114 | front_sha = Discourse.redis.get(front_commit_redis_key) 115 | @repo.reload 116 | 117 | back_cursor = Discourse.redis.get(back_cursor_redis_key) 118 | return if back_cursor == HISTORY_COMPLETE 119 | if back_cursor.present? 120 | build_history!(cursor: back_cursor) 121 | elsif front_sha.present? 122 | count = @repo.commits.count 123 | build_history!(cursor: "#{front_sha} #{count - 1}") 124 | else 125 | # this is a bad state that we should never be in 126 | # But in case it happens, easiest way to recover 127 | # is to start from scratch. 128 | @repo.commits.delete_all 129 | Discourse.redis.del(back_cursor_redis_key) 130 | Discourse.redis.del(front_commit_redis_key) 131 | end 132 | end 133 | rescue Octokit::Error => err 134 | case err 135 | when Octokit::NotFound 136 | disable_github_badges_and_inform_admin( 137 | title: I18n.t("github_commits_populator.errors.repository_not_found_pm_title"), 138 | raw: 139 | I18n.t( 140 | "github_commits_populator.errors.repository_not_found_pm", 141 | repo_name: @repo.name, 142 | base_path: Discourse.base_path, 143 | ), 144 | ) 145 | Rails.logger.warn( 146 | "Disabled github_badges_enabled site setting due to repository Not Found error ", 147 | ) 148 | when Octokit::Unauthorized 149 | disable_github_badges_and_inform_admin( 150 | title: I18n.t("github_commits_populator.errors.invalid_octokit_credentials_pm_title"), 151 | raw: 152 | I18n.t( 153 | "github_commits_populator.errors.invalid_octokit_credentials_pm", 154 | base_path: Discourse.base_path, 155 | ), 156 | ) 157 | Rails.logger.warn( 158 | "Disabled github_badges_enabled site setting due to invalid GitHub authentication credentials via github_linkback_access_token.", 159 | ) 160 | else 161 | Rails.logger.warn("#{err.class}: #{err.message}") 162 | end 163 | rescue Octokit::InvalidRepository => err 164 | disable_github_badges_and_inform_admin( 165 | title: I18n.t("github_commits_populator.errors.repository_identifier_invalid_pm_title"), 166 | raw: 167 | I18n.t( 168 | "github_commits_populator.errors.repository_identifier_invalid_pm", 169 | repo_name: @repo.name, 170 | base_path: Discourse.base_path, 171 | ), 172 | ) 173 | Rails.logger.warn( 174 | "Disabled github_badges_enabled site setting due to invalid repository identifier", 175 | ) 176 | end 177 | 178 | private 179 | 180 | def is_contribution?(commit) 181 | pr = commit.associatedPullRequests.nodes.first 182 | pr && pr.author && pr.mergedBy && pr.author.login != pr.mergedBy.login 183 | end 184 | 185 | def fetch_new_commits!(stop_at) 186 | paginator = PaginatedCommits.new(@client, @repo, page_size: 10) 187 | batch = paginator.commits 188 | done = false 189 | commits = [] 190 | recent_commits = 191 | stop_at.present? ? [] : @repo.commits.order("committed_at DESC").first(100).pluck(:sha) 192 | while !done 193 | batch.each do |c| 194 | if c.oid == stop_at || recent_commits.include?(c.oid) 195 | done = true 196 | break 197 | end 198 | commits << c 199 | end 200 | break if done 201 | paginator = paginator.next 202 | batch = paginator&.commits || [] 203 | break if batch.empty? 204 | end 205 | return if commits.size == 0 206 | existing_shas = @repo.commits.pluck(:sha) 207 | commits.reject! { |c| existing_shas.include?(c.oid) } 208 | batch_to_db(commits) 209 | set_front_commit(commits.first.oid) 210 | end 211 | 212 | # detect if a force push happened and commit is lost 213 | def removed?(sha) 214 | commit = @client.commit(@repo.name, sha) 215 | return true if commit.commit.nil? 216 | found = 217 | @client.commits(@repo.name, until: commit.commit.committer.date, page: 1, per_page: 1).first 218 | commit.sha != found.sha 219 | end 220 | 221 | def build_history!(cursor: nil) 222 | paginator = PaginatedCommits.new(@client, @repo, cursor: cursor, page_size: 100) 223 | batch = paginator.commits 224 | return if batch.empty? 225 | set_front_commit(batch.first.oid) if cursor.blank? 226 | 227 | while batch.size > 0 228 | batch_to_db(batch) 229 | next_cursor = paginator.next_cursor 230 | set_back_cursor(next_cursor) if next_cursor 231 | 232 | paginator = paginator.next 233 | batch = paginator&.commits || [] 234 | end 235 | set_back_cursor(HISTORY_COMPLETE) 236 | end 237 | 238 | def batch_to_db(batch) 239 | fragments = [] 240 | batch.each do |c| 241 | hash = commit_to_hash(c) 242 | fragments << DB.sql_fragment(<<~SQL, hash) 243 | (:repo_id, :sha, :email, :committed_at, :role_id, :merge_commit, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) 244 | SQL 245 | end 246 | DB.exec(<<~SQL) 247 | INSERT INTO github_commits 248 | (repo_id, sha, email, committed_at, role_id, merge_commit, created_at, updated_at) VALUES #{fragments.join(",")} 249 | SQL 250 | end 251 | 252 | def commit_to_hash(commit) 253 | { 254 | sha: commit.oid, 255 | email: commit.author.email, 256 | repo_id: @repo.id, 257 | committed_at: commit.committedDate, 258 | merge_commit: commit.message.match?(MERGE_COMMIT_REGEX), 259 | role_id: is_contribution?(commit) ? ROLES[:contributor] : ROLES[:committer], 260 | } 261 | end 262 | 263 | def set_front_commit(sha) 264 | Discourse.redis.set(front_commit_redis_key, sha) 265 | end 266 | 267 | def set_back_cursor(cursor) 268 | Discourse.redis.set(back_cursor_redis_key, cursor) 269 | end 270 | 271 | def front_commit_redis_key 272 | # this key should refer to the MOST RECENT commit we have in the db 273 | "discourse-github-front-commit-#{@repo.name}" 274 | end 275 | 276 | def back_cursor_redis_key 277 | # this key should refer to the cursor that lets us continue 278 | # building history from the point we reached in the previous run 279 | # that couldn't continue for whatever reasons 280 | # e.g., if we got rate-limited by github 281 | "discourse-github-back-cursor-#{@repo.name}" 282 | end 283 | 284 | def disable_github_badges_and_inform_admin(title:, raw:) 285 | SiteSetting.github_badges_enabled = false 286 | site_admin_usernames = 287 | User.where(admin: true).human_users.order("last_seen_at DESC").limit(10).pluck(:username) 288 | PostCreator.create!( 289 | Discourse.system_user, 290 | title: title, 291 | raw: raw, 292 | archetype: Archetype.private_message, 293 | target_usernames: site_admin_usernames, 294 | skip_validations: true, 295 | ) 296 | end 297 | end 298 | end 299 | -------------------------------------------------------------------------------- /app/lib/github_badges.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiscourseGithubPlugin 4 | module GithubBadges 5 | BADGE_NAME_BRONZE = "Contributor" 6 | BADGE_NAME_SILVER = "Great Contributor" 7 | BADGE_NAME_GOLD = "Amazing Contributor" 8 | 9 | COMMITTER_BADGE_NAME_BRONZE = "Committer" 10 | COMMITTER_BADGE_NAME_SILVER = "Frequent Committer" 11 | COMMITTER_BADGE_NAME_GOLD = "Amazing Committer" 12 | 13 | class Granter 14 | def initialize(emails) 15 | @emails = emails 16 | @badges = [] 17 | end 18 | 19 | def add_badge(badge, as_title:, threshold:) 20 | @badges << [badge, as_title, threshold] 21 | end 22 | 23 | def grant! 24 | email_commits = @emails.group_by { |e| e }.map { |k, l| [k, l.count] }.to_h 25 | 26 | regular_emails = [] 27 | github_name_email = {} 28 | @emails.each do |email| 29 | match = email.match(/\A(\d+\+)?(?.+)@users.noreply.github.com\Z/) 30 | 31 | if match 32 | name = match[:name] 33 | github_name_email[name] = email 34 | else 35 | regular_emails << email 36 | end 37 | end 38 | 39 | user_emails = {} 40 | User 41 | .real 42 | .where(staged: false) 43 | .with_email(regular_emails) 44 | .each { |user| user_emails[user] = user.emails } 45 | 46 | if github_name_email.any? 47 | screen_names = 48 | UserAssociatedAccount 49 | .where(provider_name: "github") 50 | .where("info ->> 'nickname' IN (?)", github_name_email.keys) 51 | .includes(:user) 52 | .map { |row| [row.user, row.info["nickname"]] } 53 | .to_h 54 | 55 | screen_names.each do |user, screen_name| 56 | user_emails[user] ||= [] 57 | user_emails[user] << github_name_email[screen_name] 58 | end 59 | end 60 | 61 | user_emails.each do |user, emails| 62 | commits_count = emails.sum { |email| email_commits[email] || 0 } 63 | @badges.each do |badge, as_title, threshold| 64 | if commits_count >= threshold && badge.enabled? && SiteSetting.enable_badges 65 | BadgeGranter.grant(badge, user) 66 | user.update!(title: badge.name) if badge.allow_title? && user.title.blank? && as_title 67 | end 68 | end 69 | end 70 | end 71 | end 72 | 73 | def self.grant! 74 | grant_committer_badges! 75 | grant_contributor_badges! 76 | end 77 | 78 | def self.grant_committer_badges! 79 | emails = 80 | GithubCommit.where(merge_commit: false, role_id: CommitsPopulator::ROLES[:committer]).pluck( 81 | :email, 82 | ) 83 | 84 | bronze, silver, gold = committer_badges 85 | 86 | granter = GithubBadges::Granter.new(emails) 87 | granter.add_badge(bronze, as_title: false, threshold: 1) 88 | granter.add_badge(silver, as_title: true, threshold: 25) 89 | granter.add_badge(gold, as_title: true, threshold: 1000) 90 | granter.grant! 91 | end 92 | 93 | def self.grant_contributor_badges! 94 | emails = 95 | GithubCommit.where( 96 | merge_commit: false, 97 | role_id: CommitsPopulator::ROLES[:contributor], 98 | ).pluck(:email) 99 | 100 | bronze, silver, gold = contributor_badges 101 | 102 | granter = GithubBadges::Granter.new(emails) 103 | granter.add_badge(bronze, as_title: false, threshold: 1) 104 | granter.add_badge( 105 | silver, 106 | as_title: true, 107 | threshold: SiteSetting.github_silver_badge_min_commits, 108 | ) 109 | granter.add_badge(gold, as_title: true, threshold: SiteSetting.github_gold_badge_min_commits) 110 | granter.grant! 111 | end 112 | 113 | def self.ensure_badge(name, attrs) 114 | badge = Badge.find_by("name ILIKE ?", name) 115 | 116 | # Check for letter-case differences 117 | badge.update!(name: name) if badge && badge.name != name 118 | 119 | badge || Badge.create!(name: name, **attrs) 120 | end 121 | 122 | def self.contributor_badges 123 | bronze = 124 | ensure_badge( 125 | BADGE_NAME_BRONZE, 126 | description: "Contributed an accepted pull request", 127 | badge_type_id: 3, 128 | default_icon: "fab-git-alt", 129 | ) 130 | 131 | silver = 132 | ensure_badge( 133 | BADGE_NAME_SILVER, 134 | description: "Contributed 25 accepted pull requests", 135 | badge_type_id: 2, 136 | default_icon: "fab-git-alt", 137 | ) 138 | 139 | gold = 140 | ensure_badge( 141 | BADGE_NAME_GOLD, 142 | description: "Contributed 250 accepted pull requests", 143 | badge_type_id: 1, 144 | default_icon: "fab-git-alt", 145 | ) 146 | 147 | [bronze, silver, gold] 148 | end 149 | 150 | def self.committer_badges 151 | bronze = 152 | ensure_badge( 153 | COMMITTER_BADGE_NAME_BRONZE, 154 | description: "Created a commit", 155 | enabled: false, 156 | badge_type_id: 3, 157 | ) 158 | 159 | silver = 160 | ensure_badge( 161 | COMMITTER_BADGE_NAME_SILVER, 162 | description: "Created 25 commits", 163 | enabled: false, 164 | badge_type_id: 2, 165 | ) 166 | 167 | gold = 168 | ensure_badge( 169 | COMMITTER_BADGE_NAME_GOLD, 170 | description: "Created 1000 commits", 171 | enabled: false, 172 | badge_type_id: 1, 173 | ) 174 | 175 | [bronze, silver, gold] 176 | end 177 | end 178 | end 179 | -------------------------------------------------------------------------------- /app/lib/github_badges_repo_setting_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class GithubBadgesRepoSettingValidator 4 | def initialize(opts = {}) 5 | @opts = opts 6 | end 7 | 8 | def valid_value?(val) 9 | return true if val.blank? 10 | val 11 | .split("|") 12 | .all? do |repo| 13 | repo.match?(DiscourseGithubPlugin::GithubRepo::VALID_URL_BASED_REPO_REGEX) || 14 | repo.match?(DiscourseGithubPlugin::GithubRepo::VALID_USER_BASED_REPO_REGEX) 15 | end 16 | end 17 | 18 | def error_message 19 | I18n.t("site_settings.errors.invalid_badge_repo") 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/lib/github_linkback.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_dependency "pretty_text" 4 | require "digest/sha1" 5 | 6 | class GithubLinkback 7 | class Link 8 | attr_reader :url, :project, :type 9 | attr_accessor :sha, :pr_number, :issue_number 10 | 11 | def initialize(url, project, type) 12 | @url = url 13 | @project = project 14 | @type = type 15 | end 16 | end 17 | 18 | def initialize(post) 19 | @post = post 20 | end 21 | 22 | def should_enqueue? 23 | !!( 24 | SiteSetting.github_linkback_enabled? && SiteSetting.enable_discourse_github_plugin? && 25 | @post.present? && @post.post_type == Post.types[:regular] && @post.raw =~ /github\.com/ && 26 | Guardian.new.can_see?(@post) && @post.topic.visible? 27 | ) 28 | end 29 | 30 | def enqueue 31 | Jobs.enqueue(:create_github_linkback, post_id: @post.id) if should_enqueue? 32 | end 33 | 34 | def github_links 35 | projects = SiteSetting.github_linkback_projects.split("|") 36 | 37 | return [] if projects.blank? 38 | 39 | result = {} 40 | PrettyText 41 | .extract_links(@post.cooked) 42 | .map(&:url) 43 | .each do |l| 44 | if l =~ %r{https?://github\.com/([^/]+)/([^/]+)/commit/([0-9a-f]+)} 45 | url, org, repo, sha = Regexp.last_match.to_a 46 | project = "#{org}/#{repo}" 47 | 48 | next if result[url] 49 | next if @post.custom_fields[GithubLinkback.field_for(url)].present? 50 | next unless is_allowed_project_link?(projects, project) 51 | 52 | link = Link.new(url, project, :commit) 53 | link.sha = sha 54 | result[url] = link 55 | elsif l =~ %r{https?://github.com/([^/]+)/([^/]+)/pull/(\d+)} 56 | url, org, repo, pr_number = Regexp.last_match.to_a 57 | project = "#{org}/#{repo}" 58 | 59 | next if result[url] 60 | next if @post.custom_fields[GithubLinkback.field_for(url)].present? 61 | next unless is_allowed_project_link?(projects, project) 62 | 63 | link = Link.new(url, project, :pr) 64 | link.pr_number = pr_number.to_i 65 | result[url] = link 66 | elsif l =~ %r{https?://github.com/([^/]+)/([^/]+)/issues/(\d+)} 67 | url, org, repo, issue_number = Regexp.last_match.to_a 68 | project = "#{org}/#{repo}" 69 | 70 | next if result[url] 71 | next if @post.custom_fields[GithubLinkback.field_for(url)].present? 72 | next unless is_allowed_project_link?(projects, project) 73 | 74 | link = Link.new(url, project, :issue) 75 | link.issue_number = issue_number.to_i 76 | result[url] = link 77 | end 78 | end 79 | result.values 80 | end 81 | 82 | def is_allowed_project_link?(projects, project) 83 | return true if projects.include?(project) 84 | 85 | check_user = project.split("/")[0] 86 | projects.any? do |allowed_project| 87 | allowed_user, allowed_all_projects = allowed_project.split("/") 88 | (allowed_user == check_user) && (allowed_all_projects == "*") 89 | end 90 | end 91 | 92 | def create 93 | return [] if SiteSetting.github_linkback_access_token.blank? 94 | 95 | links = [] 96 | 97 | DistributedMutex.synchronize("github_linkback_#{@post.id}") do 98 | links = github_links 99 | return [] if links.length() > SiteSetting.github_linkback_maximum_links 100 | 101 | links.each do |link| 102 | case link.type 103 | when :commit 104 | post_commit(link) 105 | when :pr 106 | post_pr_or_issue(link, :pr) 107 | when :issue 108 | post_pr_or_issue(link, :issue) 109 | else 110 | next 111 | end 112 | 113 | # Don't post the same link twice 114 | @post.custom_fields[GithubLinkback.field_for(link.url)] = "true" 115 | end 116 | @post.save_custom_fields 117 | end 118 | 119 | links 120 | end 121 | 122 | def self.field_for(url) 123 | "github-linkback:#{Digest::SHA1.hexdigest(url)[0..15]}" 124 | end 125 | 126 | private 127 | 128 | def post_pr_or_issue(link, type) 129 | pr_or_issue_number = link.pr_number || link.issue_number 130 | github_url = 131 | "https://api.github.com/repos/#{link.project}/issues/#{pr_or_issue_number}/comments" 132 | comment = 133 | I18n.t( 134 | type == :pr ? "github_linkback.pr_template" : "github_linkback.issue_template", 135 | title: SiteSetting.title, 136 | post_url: "#{Discourse.base_url}#{@post.url}", 137 | ) 138 | 139 | Excon.post(github_url, body: { body: comment }.to_json, headers: headers) 140 | end 141 | 142 | def post_commit(link) 143 | github_url = "https://api.github.com/repos/#{link.project}/commits/#{link.sha}/comments" 144 | 145 | comment = 146 | I18n.t( 147 | "github_linkback.commit_template", 148 | title: SiteSetting.title, 149 | post_url: "#{Discourse.base_url}#{@post.url}", 150 | ) 151 | 152 | Excon.post(github_url, body: { body: comment }.to_json, headers: headers) 153 | end 154 | 155 | def headers 156 | { 157 | "Content-Type" => "application/json", 158 | "Authorization" => "token #{SiteSetting.github_linkback_access_token}", 159 | "User-Agent" => "Discourse-Github-Linkback", 160 | } 161 | end 162 | end 163 | -------------------------------------------------------------------------------- /app/lib/github_linkback_access_token_setting_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class GithubLinkbackAccessTokenSettingValidator 4 | def initialize(opts = {}) 5 | @opts = opts 6 | end 7 | 8 | def valid_value?(val) 9 | return true if val.blank? 10 | client = Octokit::Client.new(access_token: val, per_page: 1) 11 | DiscourseGithubPlugin::GithubRepo.repos.each { |repo| client.branches(repo.name) } 12 | true 13 | rescue Octokit::Unauthorized 14 | false 15 | end 16 | 17 | def error_message 18 | I18n.t("site_settings.errors.invalid_github_linkback_access_token") 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /app/lib/github_permalinks.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ::GithubPermalinks 4 | def self.replace_github_non_permalinks(post) 5 | # replaces github non-permalinks with permalinks containing a specific commit id 6 | regex = %r{https?://github\.com/[^/]+/[^/\s]+/blob/[^\s]+}i 7 | 8 | # don't replace urls in posts that are more than 1h old 9 | return if ((Time.zone.now - post.created_at) / 60).round > 60 10 | 11 | # only run the job when post is changed by a user and it contains a github url 12 | return if (post.last_editor_id && post.last_editor_id <= 0) || !post.raw.match(regex) 13 | 14 | # make sure no other job is scheduled 15 | Jobs.cancel_scheduled_job(:replace_github_non_permalinks, post_id: post.id) 16 | 17 | # schedule the job 18 | Jobs.enqueue_in( 19 | (SiteSetting.editing_grace_period + 1).seconds.to_i, 20 | :replace_github_non_permalinks, 21 | post_id: post.id, 22 | bypass_bump: false, 23 | ) 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/models/github_commit.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiscourseGithubPlugin 4 | class GithubCommit < ActiveRecord::Base 5 | belongs_to :repo, class_name: :GithubRepo 6 | end 7 | end 8 | 9 | # == Schema Information 10 | # 11 | # Table name: github_commits 12 | # 13 | # id :bigint not null, primary key 14 | # repo_id :bigint not null 15 | # sha :string(40) not null 16 | # email :string(513) not null 17 | # committed_at :datetime not null 18 | # role_id :integer not null 19 | # merge_commit :boolean default(FALSE), not null 20 | # created_at :datetime not null 21 | # updated_at :datetime not null 22 | # 23 | # Indexes 24 | # 25 | # index_github_commits_on_repo_id (repo_id) 26 | # 27 | -------------------------------------------------------------------------------- /app/models/github_repo.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiscourseGithubPlugin 4 | class GithubRepo < ActiveRecord::Base 5 | VALID_URL_BASED_REPO_REGEX = %r{https?://github.com/(.+)} 6 | VALID_USER_BASED_REPO_REGEX = Octokit::Repository::NAME_WITH_OWNER_PATTERN 7 | 8 | has_many :commits, foreign_key: :repo_id, class_name: :GithubCommit, dependent: :destroy 9 | 10 | def self.repos 11 | repos = [] 12 | SiteSetting 13 | .github_badges_repos 14 | .split("|") 15 | .each do |link| 16 | name = match_name_from_setting(link) 17 | next if name.blank? 18 | name.gsub!(/\.git$/, "") 19 | name.gsub!(%r{/$}, "") # Remove trailing '/' 20 | repos << find_or_create_by!(name: name) 21 | end 22 | repos 23 | end 24 | 25 | def self.match_name_from_setting(repo) 26 | if repo =~ VALID_URL_BASED_REPO_REGEX 27 | Regexp.last_match[1] 28 | elsif repo =~ VALID_USER_BASED_REPO_REGEX 29 | repo_name = Regexp.last_match[0] 30 | repo.match(/https?:/).blank? ? repo_name : nil 31 | end 32 | end 33 | end 34 | end 35 | 36 | # == Schema Information 37 | # 38 | # Table name: github_repos 39 | # 40 | # id :bigint not null, primary key 41 | # name :string(255) not null 42 | # created_at :datetime not null 43 | # updated_at :datetime not null 44 | # 45 | # Indexes 46 | # 47 | # index_github_repos_on_name (name) UNIQUE 48 | # 49 | -------------------------------------------------------------------------------- /config/locales/client.ar.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | ar: 8 | admin_js: 9 | admin: 10 | site_settings: 11 | categories: 12 | discourse_github: "Discourse GitHub" 13 | replace_github_link: 14 | edit_reason: "تم استبدال رابط Github برابط دائم" 15 | -------------------------------------------------------------------------------- /config/locales/client.be.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | be: 8 | -------------------------------------------------------------------------------- /config/locales/client.bg.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | bg: 8 | -------------------------------------------------------------------------------- /config/locales/client.bs_BA.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | bs_BA: 8 | -------------------------------------------------------------------------------- /config/locales/client.ca.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | ca: 8 | -------------------------------------------------------------------------------- /config/locales/client.cs.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | cs: 8 | admin_js: 9 | admin: 10 | site_settings: 11 | categories: 12 | discourse_github: "Discourse GitHub" 13 | replace_github_link: 14 | edit_reason: "Odkaz na Github byl nahrazen trvalým odkazem" 15 | -------------------------------------------------------------------------------- /config/locales/client.da.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | da: 8 | -------------------------------------------------------------------------------- /config/locales/client.de.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | de: 8 | admin_js: 9 | admin: 10 | site_settings: 11 | categories: 12 | discourse_github: "Discourse – GitHub" 13 | replace_github_link: 14 | edit_reason: "Github-Link wurde durch einen permanenten Link ersetzt" 15 | -------------------------------------------------------------------------------- /config/locales/client.el.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | el: 8 | -------------------------------------------------------------------------------- /config/locales/client.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | admin_js: 3 | admin: 4 | site_settings: 5 | categories: 6 | discourse_github: "Discourse GitHub" 7 | replace_github_link: 8 | edit_reason: "Github link was replaced with a permanent link" 9 | -------------------------------------------------------------------------------- /config/locales/client.en_GB.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | en_GB: 8 | -------------------------------------------------------------------------------- /config/locales/client.es.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | es: 8 | admin_js: 9 | admin: 10 | site_settings: 11 | categories: 12 | discourse_github: "GitHub de Discourse" 13 | replace_github_link: 14 | edit_reason: "El enlace de Github ha sido sustituido por un enlace permanente" 15 | -------------------------------------------------------------------------------- /config/locales/client.et.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | et: 8 | -------------------------------------------------------------------------------- /config/locales/client.fa_IR.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | fa_IR: 8 | replace_github_link: 9 | edit_reason: "پیوند گیت‌هاب با یک پیوند دائمی جایگزین شد" 10 | -------------------------------------------------------------------------------- /config/locales/client.fi.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | fi: 8 | admin_js: 9 | admin: 10 | site_settings: 11 | categories: 12 | discourse_github: "Discourse GitHub" 13 | replace_github_link: 14 | edit_reason: "Github-linkki korvattiin pysyvällä linkillä" 15 | -------------------------------------------------------------------------------- /config/locales/client.fr.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | fr: 8 | admin_js: 9 | admin: 10 | site_settings: 11 | categories: 12 | discourse_github: "Discourse GitHub" 13 | replace_github_link: 14 | edit_reason: "Le lien Github a été remplacé par un lien permanent" 15 | -------------------------------------------------------------------------------- /config/locales/client.gl.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | gl: 8 | -------------------------------------------------------------------------------- /config/locales/client.he.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | he: 8 | admin_js: 9 | admin: 10 | site_settings: 11 | categories: 12 | discourse_github: "Discourse GitHub" 13 | replace_github_link: 14 | edit_reason: "הקישור ל־GitHub הוחלף בקישור קבוע" 15 | -------------------------------------------------------------------------------- /config/locales/client.hr.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | hr: 8 | -------------------------------------------------------------------------------- /config/locales/client.hu.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | hu: 8 | -------------------------------------------------------------------------------- /config/locales/client.hy.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | hy: 8 | -------------------------------------------------------------------------------- /config/locales/client.id.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | id: 8 | -------------------------------------------------------------------------------- /config/locales/client.it.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | it: 8 | admin_js: 9 | admin: 10 | site_settings: 11 | categories: 12 | discourse_github: "Discourse GitHub" 13 | replace_github_link: 14 | edit_reason: "Il collegamento Github è stato sostituito con un collegamento permanente" 15 | -------------------------------------------------------------------------------- /config/locales/client.ja.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | ja: 8 | admin_js: 9 | admin: 10 | site_settings: 11 | categories: 12 | discourse_github: "Discourse GitHub" 13 | replace_github_link: 14 | edit_reason: "GitHub リンクはパーマリンクに置き換えられました" 15 | -------------------------------------------------------------------------------- /config/locales/client.ko.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | ko: 8 | -------------------------------------------------------------------------------- /config/locales/client.lt.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | lt: 8 | -------------------------------------------------------------------------------- /config/locales/client.lv.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | lv: 8 | -------------------------------------------------------------------------------- /config/locales/client.nb_NO.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | nb_NO: 8 | -------------------------------------------------------------------------------- /config/locales/client.nl.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | nl: 8 | admin_js: 9 | admin: 10 | site_settings: 11 | categories: 12 | discourse_github: "Discourse GitHub" 13 | replace_github_link: 14 | edit_reason: "Github-link is vervangen door een permanente link" 15 | -------------------------------------------------------------------------------- /config/locales/client.pl_PL.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | pl_PL: 8 | replace_github_link: 9 | edit_reason: "Link do Githuba został zastąpiony stałym linkiem" 10 | -------------------------------------------------------------------------------- /config/locales/client.pt.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | pt: 8 | -------------------------------------------------------------------------------- /config/locales/client.pt_BR.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | pt_BR: 8 | admin_js: 9 | admin: 10 | site_settings: 11 | categories: 12 | discourse_github: "Discourse GitHub" 13 | replace_github_link: 14 | edit_reason: "O link do Github foi subtituído por um link permanente" 15 | -------------------------------------------------------------------------------- /config/locales/client.ro.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | ro: 8 | -------------------------------------------------------------------------------- /config/locales/client.ru.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | ru: 8 | admin_js: 9 | admin: 10 | site_settings: 11 | categories: 12 | discourse_github: "Плагин GitHub для Discourse" 13 | replace_github_link: 14 | edit_reason: "Ссылка на Github была заменена на постоянную ссылку" 15 | -------------------------------------------------------------------------------- /config/locales/client.sk.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | sk: 8 | -------------------------------------------------------------------------------- /config/locales/client.sl.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | sl: 8 | -------------------------------------------------------------------------------- /config/locales/client.sq.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | sq: 8 | -------------------------------------------------------------------------------- /config/locales/client.sr.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | sr: 8 | -------------------------------------------------------------------------------- /config/locales/client.sv.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | sv: 8 | replace_github_link: 9 | edit_reason: "Github-länken ersattes med en permanent länk" 10 | -------------------------------------------------------------------------------- /config/locales/client.sw.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | sw: 8 | -------------------------------------------------------------------------------- /config/locales/client.te.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | te: 8 | -------------------------------------------------------------------------------- /config/locales/client.th.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | th: 8 | -------------------------------------------------------------------------------- /config/locales/client.tr_TR.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | tr_TR: 8 | admin_js: 9 | admin: 10 | site_settings: 11 | categories: 12 | discourse_github: "Discourse GitHub" 13 | replace_github_link: 14 | edit_reason: "Github bağlantısı kalıcı bir bağlantıyla değiştirildi" 15 | -------------------------------------------------------------------------------- /config/locales/client.ug.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | ug: 8 | -------------------------------------------------------------------------------- /config/locales/client.uk.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | uk: 8 | -------------------------------------------------------------------------------- /config/locales/client.ur.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | ur: 8 | -------------------------------------------------------------------------------- /config/locales/client.vi.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | vi: 8 | -------------------------------------------------------------------------------- /config/locales/client.zh_CN.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | zh_CN: 8 | admin_js: 9 | admin: 10 | site_settings: 11 | categories: 12 | discourse_github: "Discourse GitHub" 13 | replace_github_link: 14 | edit_reason: "Github 链接被替换为永久链接" 15 | -------------------------------------------------------------------------------- /config/locales/client.zh_TW.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | zh_TW: 8 | -------------------------------------------------------------------------------- /config/locales/server.ar.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | ar: 8 | site_settings: 9 | enable_discourse_github_plugin: "تفعيل المكوِّن الإضافي discourse-github" 10 | github_linkback_enabled: "ربط مشكلات GitHub بمناقشات المنتدى" 11 | github_linkback_projects: "قائمة المشروعات التي يمكن الربط بها" 12 | github_linkback_access_token: 'رمز وصول صالح للمستخدم الذي سينشر الرابط ولإحصاء الإرسالات/المساهمات لمنح الشارات. راجع هنا للحصول على الإرشادات بشأن كيفية الحصول على رمز مميَّز.' 13 | github_linkback_maximum_links: "الحد الأقصى لعدد الروابط التي يمكن إنشاؤها من منشور واحد. عندما تحتوي إحدى المشاركات على روابط أكثر من الحد، لا يتم إنشاء أيٍّ منها." 14 | github_permalinks_enabled: "تفعيل استبدال الرابط الثابت من GitHub" 15 | github_permalinks_exclude: "اسم الملف أو الدليل الذي يجب استبعاده من استبدال الرابط الثابت. أدخل اسم الملف أو المسار الكامل فقط: user/repository/optional-directory/filename" 16 | github_badges_enabled: "تفعيل شارات GitHub" 17 | github_badges_repos: "عناوين URL الخاصة بمستودعات GitHub للبحث عن المساهمات والإرسالات" 18 | github_silver_badge_min_commits: "أقل عدد من الإرسالات للحصول على الشارة الفضية" 19 | github_gold_badge_min_commits: "أقل عدد من الإرسالات للحصول على الشارة الذهبية" 20 | errors: 21 | invalid_badge_repo: "يجب عليك إدخال عنوان URL على GitHub أو اسم المستودع بالتنسيق github_user/repository_name" 22 | invalid_github_linkback_access_token: "يجب عليك إدخال رمز وصول صالح إلى رابط GitHub والذي يمكنه الوصول إلى مستودعات الشارة التي أدخلتها." 23 | github_linkback: 24 | commit_template: | 25 | تم ذكر هذا الإرسال في **%{title}**. قد تكون هناك تفاصيل ذات صلة هناك: 26 | 27 | %{post_url} 28 | pr_template: | 29 | تم ذكر طلب السحب هذا في **%{title}**. قد تكون هناك تفاصيل ذات صلة هناك: 30 | 31 | %{post_url} 32 | issue_template: | 33 | تم ذكر هذه المشكلة في **%{title}**. قد تكون هناك تفاصيل ذات صلة هناك: 34 | 35 | %{post_url} 36 | github_commits_populator: 37 | errors: 38 | repository_identifier_invalid_pm_title: "الإجراء المطلوب للمكوِّن الإضافي discourse-github" 39 | repository_identifier_invalid_pm: | 40 | المستودع المحدَّد في المكوِّن الإضافي discourse-github غير صالح: 41 | %{repo_name} 42 | 43 | يجب تحديده في صورة `user/repo`. 44 | 45 | لن يتم منح الشارات حتى يتم تصحيح الاسم في 46 | إعدادات الموقع لـ discourse-github 47 | وإعادة تشغيل "github_badges_enabled". 48 | repository_not_found_pm_title: "الإجراء المطلوب للمكوِّن الإضافي discourse-github" 49 | repository_not_found_pm: | 50 | أرجع المستودع المحدَّد في المكوِّن الإضافي discourse-github رسالة الخطأ "غير موجود": 51 | %{repo_name} 52 | 53 | يجب تحديده في صورة `user/repo`. 54 | 55 | لن يتم منح الشارات حتى يتم تصحيح الاسم في 56 | إعدادات الموقع لـ discourse-github 57 | وإعادة تشغيل "github_badges_enabled". 58 | invalid_octokit_credentials_pm_title: "الإجراء المطلوب للمكوِّن الإضافي discourse-github" 59 | invalid_octokit_credentials_pm: | 60 | بيانات اعتماد GitHub التي أدخلتها للمكوِّن الإضافي discourse-github غير صالحة. تم تعطيل إعداد الموقع "github badges enabled"، ولن يتم ملء الإرسالات بعد الآن حتى يتم حل المشكلة. 61 | 62 | تحقَّق من إعداد الموقع "رمز الوصول إلى رابط github" للتأكُّد من صحة الرمز المميَّز وأن لديه إذن لجميع المستودعات عبر 63 | إعدادات الموقع لـ discourse-github. 64 | -------------------------------------------------------------------------------- /config/locales/server.be.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | be: 8 | -------------------------------------------------------------------------------- /config/locales/server.bg.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | bg: 8 | -------------------------------------------------------------------------------- /config/locales/server.bs_BA.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | bs_BA: 8 | -------------------------------------------------------------------------------- /config/locales/server.ca.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | ca: 8 | -------------------------------------------------------------------------------- /config/locales/server.cs.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | cs: 8 | site_settings: 9 | enable_discourse_github_plugin: "Povolit discourse-github plugin" 10 | github_linkback_enabled: "Propojit GitHub issues a diskuse na fóru" 11 | github_linkback_projects: "Seznam projektů, které chcete propojit" 12 | github_linkback_access_token: 'Platný přístupový token pro uživatele, který bude zveřejňovat zpětný odkaz a počítat commity/příspěvky pro udělování odznaků. Pokyny k získání tokenu naleznete zde.' 13 | github_linkback_maximum_links: "Maximální počet zpětných odkazů, které lze vytvořit z jednoho příspěvku. Když příspěvek obsahuje více odkazů, žádný se nevytvoří." 14 | github_permalinks_enabled: "Povolení přepisování trvalých odkazů GitHubu" 15 | github_permalinks_exclude: "Název souboru nebo adresáře, který má být vyloučen z přepisování trvalých odkazů. Zadejte pouze název souboru nebo celou cestu: user/repository/optional-directory/filename." 16 | github_badges_enabled: "Povolit odznaky GitHub" 17 | github_badges_repos: "Adresy URL repozitářů GitHub pro vyhledávání příspěvků a commitů." 18 | github_silver_badge_min_commits: "Minimální počet commitů pro stříbrný odznak" 19 | github_gold_badge_min_commits: "Minimální počet commitů pro zlatý odznak" 20 | errors: 21 | invalid_badge_repo: "Musíte zadat adresu URL GitHubu nebo název úložiště ve formátu github_user/repository_name" 22 | invalid_github_linkback_access_token: "Musíte poskytnout platný přístupový token pro zpětné odkazy GitHub, který má přístup k repozitářům odznaků, které jste poskytli." 23 | github_linkback: 24 | commit_template: | 25 | Tento commit byl zmíněn na **%{title}**. Mohou tam být relevantní podrobnosti: 26 | 27 | %{post_url} 28 | pr_template: | 29 | Tento pull request byl zmíněn na **%{title}**. Možná tam existují relevantní podrobnosti: 30 | 31 | %{post_url} 32 | issue_template: | 33 | Tento problém byl zmíněn na **%{title}**. Tam mohou být relevantní podrobnosti: 34 | 35 | %{post_url} 36 | github_commits_populator: 37 | errors: 38 | repository_identifier_invalid_pm_title: "Pro plugin discourse-github je vyžadována akce" 39 | repository_identifier_invalid_pm: | 40 | Repozitář zadaný v pluginu discourse-github je neplatný: 41 | %{repo_name} 42 | 43 | Měl by být zadán jako `user/repo`. 44 | 45 | Odznaky nebudou uděleny, dokud nebude název opraven v 46 | nastavení webu discourse-github 47 | a nebude znovu zapnuto „github_badges_enabled“. 48 | repository_not_found_pm_title: "Pro plugin discourse-github je vyžadována akce" 49 | repository_not_found_pm: | 50 | Repozitář uvedený v pluginu discourse-github vrátil chybu "Not Found" 51 | : %{repo_name} 52 | 53 | odznaky nebudou uděleny, dokud nebude název opraven v 54 | nastavení webu discourse-github 55 | a nebude znovu zapnuto "github_badges_enabled". 56 | invalid_octokit_credentials_pm_title: "Pro plugin discourse-github je vyžadována akce" 57 | invalid_octokit_credentials_pm: "Pověření GitHub poskytnutá pluginu discourse-github jsou neplatná. Nastavení \"github badges enabled\" \nbylo deaktivováno a commity již nebudou vyplňovány, dokud nebude problém vyřešen.\n\nZkontrolujte nastavení webu „github linkback access token“, abyste se ujistili, že token je správný a má oprávnění ke všem úložištím\nprostřednictvím\nnastavení webu discourse-github.\n" 58 | -------------------------------------------------------------------------------- /config/locales/server.da.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | da: 8 | -------------------------------------------------------------------------------- /config/locales/server.de.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | de: 8 | site_settings: 9 | enable_discourse_github_plugin: "Aktiviere das discourse-github-Plug-in" 10 | github_linkback_enabled: "GitHub-Themen mit Forumsdiskussionen verlinken" 11 | github_linkback_projects: "Liste der Projekte, von denen zurückverlinkt werden soll" 12 | github_linkback_access_token: 'Ein gültiges Zugriffstoken für den Benutzer, der den Linkback veröffentlicht, und für das Zählen von Commits/Beiträgen, um Abzeichen zu vergeben. Siehe hier für Anweisungen, wie du ein Token bekommst.' 13 | github_linkback_maximum_links: "Maximale Anzahl von Linkbacks, die aus einem einzigen Beitrag erstellt werden können. Wenn ein Beitrag mehr Links enthält, werden keine erstellt." 14 | github_permalinks_enabled: "Überschreiben von GitHub-Permalinks aktivieren" 15 | github_permalinks_exclude: "Dateiname oder Verzeichnis, der/das vom Überschreiben von Permalinks ausgeschlossen werden soll. Gib nur den Dateinamen oder den vollständigen Pfad an: user/repository/optional-directory/filename" 16 | github_badges_enabled: "GitHub-Abzeichen aktivieren" 17 | github_badges_repos: "URLs der GitHub-Repos, die nach Beiträgen und Commits durchsucht werden sollen" 18 | github_silver_badge_min_commits: "Mindestanzahl an Commits für das Silberabzeichen" 19 | github_gold_badge_min_commits: "Mindestanzahl an Commits für das Goldabzeichen" 20 | errors: 21 | invalid_badge_repo: "Du musst eine GitHub-URL oder den Repository-Namen im Format github_user/repository_name angeben" 22 | invalid_github_linkback_access_token: "Du musst ein gültiges GitHub-Linkback-Access-Token angeben, das Zugriff auf die von dir angegebenen Abzeichen-Repositories hat." 23 | github_linkback: 24 | commit_template: | 25 | Dieser Commit wurde auf **%{title}** erwähnt. Vielleicht gibt es dort relevante Details: 26 | 27 | %{post_url} 28 | pr_template: | 29 | Dieser PR wurde auf **%{title}** erwähnt. Vielleicht gibt es dort relevante Details: 30 | 31 | %{post_url} 32 | issue_template: | 33 | Dieses Thema wurde auf **%{title}** erwähnt. Vielleicht gibt es dort relevante Details: 34 | 35 | %{post_url} 36 | github_commits_populator: 37 | errors: 38 | repository_identifier_invalid_pm_title: "Aktion erforderlich für discourse-github-Plug-in" 39 | repository_identifier_invalid_pm: | 40 | Ein im discourse-github-Plug-in angegebenes Repository ist ungültig: 41 | %{repo_name} 42 | 43 | Es muss als `user/repo` angegeben werden. 44 | 45 | Abzeichen werden erst vergeben, nachdem der Name in den 46 | Website-Einstellungen für discourse-github 47 | korrigiert und „github_badges_enabled“ wieder aktiviert wurde. 48 | repository_not_found_pm_title: "Aktion erforderlich für discourse-github-Plug-in" 49 | repository_not_found_pm: | 50 | Ein im discourse-github-Plug-in angegebenes Repository wurde nicht gefunden: 51 | %{repo_name} 52 | 53 | Abzeichen werden erst vergeben, nachdem der Name in den 54 | Website-Einstellungen für discourse-github 55 | korrigiert und „github_badges_enabled“ wieder aktiviert wurde. 56 | invalid_octokit_credentials_pm_title: "Aktion erforderlich für discourse-github-Plug-in" 57 | invalid_octokit_credentials_pm: | 58 | Die GitHub-Anmeldedaten, die für das discourse-github-Plug-in angegeben wurden, sind ungültig. Die Website-Einstellung „github badges enabled“ 59 | wurde deaktiviert und Commits werden nicht mehr übertragen, bis das Problem behoben ist. 60 | 61 | Überprüfe deine Website-Einstellung „github linkback access token“, um sicherzustellen, dass das Token korrekt ist und über Zugriffsrechte auf alle Repos verfügt. Besuche hierzu die 62 | Website-Einstellungen für discourse-github. 63 | -------------------------------------------------------------------------------- /config/locales/server.el.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | el: 8 | -------------------------------------------------------------------------------- /config/locales/server.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | site_settings: 3 | enable_discourse_github_plugin: "Enable the discourse-github plugin" 4 | github_linkback_enabled: "Link GitHub issues back to forum discussions" 5 | github_linkback_projects: "List of projects to link back from" 6 | github_linkback_access_token: 'A valid access token for the user who will post the linkback and for counting commits/contributions to grant badges. See here for instructions on how to get a token.' 7 | github_linkback_maximum_links: "Maximum number of linkbacks to create from one single post. When a post contains more links, none is created." 8 | github_permalinks_enabled: "Enable GitHub permalink overwrites" 9 | github_permalinks_exclude: "Filename or directory that should be excluded from permalink overwrites. Provide only filename or full path: user/repository/optional-directory/filename" 10 | github_badges_enabled: "Enable GitHub badges" 11 | github_badges_repos: "URLs of the GitHub repos to scan for contributions and commits" 12 | github_silver_badge_min_commits: "Minumum number of commits for silver badge" 13 | github_gold_badge_min_commits: "Minumum number of commits for gold badge" 14 | 15 | errors: 16 | invalid_badge_repo: "You must provide a GitHub URL or the repository name in the format github_user/repository_name" 17 | invalid_github_linkback_access_token: "You must provide a valid GitHub linkback access token which has access to the badge repositories you have provided." 18 | 19 | github_linkback: 20 | commit_template: | 21 | This commit has been mentioned on **%{title}**. There might be relevant details there: 22 | 23 | %{post_url} 24 | pr_template: | 25 | This pull request has been mentioned on **%{title}**. There might be relevant details there: 26 | 27 | %{post_url} 28 | issue_template: | 29 | This issue has been mentioned on **%{title}**. There might be relevant details there: 30 | 31 | %{post_url} 32 | github_commits_populator: 33 | errors: 34 | repository_identifier_invalid_pm_title: "Action required for discourse-github plugin" 35 | repository_identifier_invalid_pm: | 36 | A repository specified in the discourse-github plugin is invalid: 37 | %{repo_name} 38 | 39 | They should be specified as `user/repo`. 40 | 41 | Badges will not be awarded until the name is corrected in 42 | discourse-github Site Settings 43 | and "github_badges_enabled" is turned back on. 44 | repository_not_found_pm_title: "Action required for discourse-github plugin" 45 | repository_not_found_pm: | 46 | A repository specified in the discourse-github plugin returned a "Not Found" 47 | error: %{repo_name} 48 | 49 | Badges will not be awarded until the name is corrected in 50 | discourse-github Site Settings 51 | and "github_badges_enabled" is turned back on. 52 | invalid_octokit_credentials_pm_title: "Action required for discourse-github plugin" 53 | invalid_octokit_credentials_pm: | 54 | The GitHub credentials provided to the discourse-github plugin are invalid. The "github badges enabled\ site 55 | setting has been disabled, and commits will no longer be populated until the issue is resolved. 56 | 57 | Check your "github linkback access token" site setting to ensure the token is correct and has permission to all 58 | repos via 59 | discourse-github Site Settings. 60 | -------------------------------------------------------------------------------- /config/locales/server.en_GB.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | en_GB: 8 | -------------------------------------------------------------------------------- /config/locales/server.es.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | es: 8 | site_settings: 9 | enable_discourse_github_plugin: "Habilitar el plugin discourse-github" 10 | github_linkback_enabled: "Vincular problemas de GitHub a discusiones del foro" 11 | github_linkback_projects: "Lista de proyectos desde los que enlazar" 12 | github_linkback_access_token: 'Un token de acceso válido para el usuario que publicará el enlace de regreso y para contar commits/contribuciones para conceder insignias. Puedes obtener instrucciones sobre cómo obtener un token aquí.' 13 | github_linkback_maximum_links: "Número máximo de enlaces a crear a partir de una sola entrada. Cuando una entrada contiene más enlaces, no se crea ninguno." 14 | github_permalinks_enabled: "Habilitar sobrescrituras de enlaces permanentes de GitHub" 15 | github_permalinks_exclude: "Nombre de archivo o directorio que debe excluirse de las sobrescrituras de enlaces permanentes. Proporciona solo el nombre de archivo o la ruta completa: usuario/repositorio/directorio-opcional/nombre_del_archivo" 16 | github_badges_enabled: "Habilitar insignias de GitHub" 17 | github_badges_repos: "URL de los repositorios de GitHub para buscar contribuciones y commits" 18 | github_silver_badge_min_commits: "Número mínimo de commits para la insignia de plata" 19 | github_gold_badge_min_commits: "Número mínimo de commits para la insignia de oro" 20 | errors: 21 | invalid_badge_repo: "Debes proporcionar una URL de GitHub o el nombre del repositorio en el formato github_user/nombre_del_repositorio" 22 | invalid_github_linkback_access_token: "Debes proporcionar un token de acceso de enlace de retorno de GitHub válido que tenga acceso a los repositorios de insignias que has proporcionado." 23 | github_linkback: 24 | commit_template: | 25 | Este commit ha sido mencionado en **%{title}**. Puede que haya detalles relevantes allí: 26 | 27 | %{post_url} 28 | pr_template: | 29 | Esta solicitud de extracción ha sido mencionada en **%{title}**. Puede que haya detalles relevantes allí: 30 | 31 | %{post_url} 32 | issue_template: | 33 | Este problema ha sido mencionado en **%{title}**. Puede que haya detalles relevantes allí: 34 | 35 | %{post_url} 36 | github_commits_populator: 37 | errors: 38 | repository_identifier_invalid_pm_title: "Acción requerida para el plugin discourse-github" 39 | repository_identifier_invalid_pm: | 40 | Un repositorio especificado en el plugin discourse-github no es válido: 41 | %{repo_name} 42 | 43 | Deben especificarse como `user/repo`. 44 | 45 | No se concederán insignias hasta que se corrija el nombre en 46 | los Ajustes del sitio de discourse-github 47 | y «github_badges_enabled» esté habilitado de nuevo. 48 | repository_not_found_pm_title: "Acción requerida para el plugin discourse-github" 49 | repository_not_found_pm: | 50 | Un repositorio especificado en el plugin discourse-github ha devuelto un error 51 | «No encontrado»: %{repo_name} 52 | 53 | No se concederán insignias hasta que se corrija el nombre en 54 | los Ajustes del sitio de discourse-github 55 | y «github_badges_enabled» esté habilitado de nuevo. 56 | invalid_octokit_credentials_pm_title: "Acción requerida para el plugin discourse-github" 57 | invalid_octokit_credentials_pm: | 58 | Las credenciales de GitHub proporcionadas al plugin discourse-github no son válidas. El ajuste «github badges enabled\ site» 59 | ha sido deshabilitada, y los commits ya no se poblarán hasta que el problema sea resuelto. 60 | 61 | Comprueba la configuración de tu «token de acceso al enlace de retorno de github» para asegurarte de que el token es correcto y tienes permiso para todos los 62 | repositorios a través de 63 | los Ajustes del sitio de discourse-github. 64 | -------------------------------------------------------------------------------- /config/locales/server.et.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | et: 8 | -------------------------------------------------------------------------------- /config/locales/server.fa_IR.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | fa_IR: 8 | site_settings: 9 | enable_discourse_github_plugin: "افزونه discourse-github را فعال کنید" 10 | github_permalinks_enabled: "فعال کردن بازنویسی پیوند دائمی گیت‌هاب" 11 | github_badges_enabled: "فعال کردن نشان‌های گیت‌هاب" 12 | -------------------------------------------------------------------------------- /config/locales/server.fi.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | fi: 8 | site_settings: 9 | enable_discourse_github_plugin: "Ota discourse-github-lisäosa käyttöön" 10 | github_linkback_enabled: "Liitä GitHub-ongelmat foorumin keskusteluihin" 11 | github_linkback_projects: "Luettelo projekteista, joista voit linkittää takaisin" 12 | github_linkback_access_token: 'Kelvollinen käyttöoikeustietue käyttäjälle, joka julkaisee paluulinkin, ja solmujen/kontribuutioiden laskemiseen kunniamerkkien myöntämiseksi. Katso ohjeita tietueen saamiseen täältä.' 13 | github_linkback_maximum_links: "Yhdestä viestistä julkaistavien paluulinkkien enimmäismäärä. Kun viesti sisältää enemmän linkkejä, yhtään ei luoda." 14 | github_permalinks_enabled: "Ota GitHubin pysyvien linkkien korvaaminen käyttöön" 15 | github_permalinks_exclude: "Tiedostonimi tai hakemisto, joka tulee sulkea pois pysyvien linkkien korvaamisesta. Anna vain tiedostonimi tai täydellinen polku: käyttäjä/tietovarasto/valinnainen-hakemisto/tiedostonimi" 16 | github_badges_enabled: "Ota GitHub-kunniamerkit käyttöön" 17 | github_badges_repos: "GitHub-tietovarastojen URL-osoitteet, joista etsitään kontribuutioita ja solmuja" 18 | github_silver_badge_min_commits: "Solmujen vähimmäismäärä hopeakunniamerkin saamiseksi" 19 | github_gold_badge_min_commits: "Solmujen vähimmäismäärä kultakunniamerkin saamiseksi" 20 | errors: 21 | invalid_badge_repo: "Sinun täytyy antaa GitHubin URL-osoite tai tietovaraston nimi muodossa github_käyttäjä/tietovaraston_nimi" 22 | invalid_github_linkback_access_token: "Sinun täytyy antaa kelvollinen GitHub-paluulinkin käyttöoikeustietue, jolla on antamiesi kunniamerkkitietovarastojen käyttöoikeus." 23 | github_linkback: 24 | commit_template: | 25 | Tämä solmu on mainittu ketjussa **%{title}**. Sieltä voi löytyä olennaisia tietoja: 26 | 27 | %{post_url} 28 | pr_template: | 29 | Tämä vetopyyntö on mainittu ketjussa **%{title}**. Sieltä voi löytyä olennaisia tietoja: 30 | 31 | %{post_url} 32 | issue_template: | 33 | Tämä seikka on mainittu ketjussa **%{title}**. Sieltä voi löytyä olennaisia tietoja: 34 | 35 | %{post_url} 36 | github_commits_populator: 37 | errors: 38 | repository_identifier_invalid_pm_title: "Discourse-github-lisäosa edellyttää toimenpiteitä" 39 | repository_identifier_invalid_pm: | 40 | Discourse-github-lisäosassa määritetty tietovarasto on virheellinen: 41 | %{repo_name} 42 | 43 | Ne tulisi määrittää muodossa "käyttäjä/tietovarasto". 44 | 45 | Kunniamerkkejä ei myönnetä ennen kuin nimi on korjattu 46 | discourse-githubin sivustoasetuksissa 47 | ja "github_badges_enabled" on otettu käyttöön uudelleen. 48 | repository_not_found_pm_title: "Discourse-github-lisäosa edellyttää toimenpiteitä" 49 | repository_not_found_pm: | 50 | Discourse-github-lisäosassa määritetty tietovarasto palautti "ei löydy" -virheen: 51 | %{repo_name} 52 | 53 | Kunniamerkkejä ei myönnetä ennen kuin nimi on korjattu 54 | discourse-githubin sivustoasetuksissa 55 | ja "github_badges_enabled" on otettu käyttöön uudelleen. 56 | invalid_octokit_credentials_pm_title: "Discourse-github-lisäosa edellyttää toimenpiteitä" 57 | invalid_octokit_credentials_pm: | 58 | Discourse-github-lisäosalle annetut GitHubin tunnistetiedot ovat virheellisiä. "Github badges enabled" -sivustoasetus on poistettu käytöstä, eikä solmuja enää täytetä ennen kuin ongelma on ratkaistu. 59 | 60 | Tarkista "github linkback access token" -sivustoasetuksesi varmistaaksesi, että tietue on oikea ja että sillä on kaikkien tietovarastojen käyttöoikeus discourse-githubin sivustoasetuksissa. 61 | -------------------------------------------------------------------------------- /config/locales/server.fr.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | fr: 8 | site_settings: 9 | enable_discourse_github_plugin: "Activer l'extension discourse-github" 10 | github_linkback_enabled: "Relier les problèmes de GitHub aux discussions du forum" 11 | github_linkback_projects: "Liste des projets à partir desquels effectuer le lien" 12 | github_linkback_access_token: 'Un jeton d''accès valide pour l''utilisateur qui publiera le lien et pour compter les commits/contributions pour accorder des badges. Consultez cette page pour savoir comment obtenir un jeton.' 13 | github_linkback_maximum_links: "Nombre maximal de liens à créer à partir d'un seul message. Lorsqu'un message contient plus de liens, aucun d'entre eux n'est créé." 14 | github_permalinks_enabled: "Activer les écrasements de permaliens GitHub" 15 | github_permalinks_exclude: "Nom de fichier ou répertoire à exclure des remplacements de permaliens. Indiquez uniquement le nom de fichier ou le chemin d'accès complet : user/repository/optional-directory/filename" 16 | github_badges_enabled: "Activer les badges GitHub" 17 | github_badges_repos: "Adresses URL des dépôts GitHub pour rechercher les contributions et les commits" 18 | github_silver_badge_min_commits: "Nombre minimal de commits pour le badge d'argent" 19 | github_gold_badge_min_commits: "Nombre minimal de commits pour le badge d'or" 20 | errors: 21 | invalid_badge_repo: "Vous devez fournir une adresse URL GitHub ou le nom du dépôt au format github_user/repository_name" 22 | invalid_github_linkback_access_token: "Vous devez fournir un jeton d'accès de lien GitHub valide qui donne accès aux dépôts de badges que vous avez fournis." 23 | github_linkback: 24 | commit_template: | 25 | Ce commit a été mentionné sur **%{title}**. Il pourrait y avoir des détails pertinents ici : 26 | 27 | %{post_url} 28 | pr_template: | 29 | Cette requête pull a été mentionnée sur **%{title}**. Il pourrait y avoir des détails pertinents ici : 30 | 31 | %{post_url} 32 | issue_template: | 33 | Ce problème a été mentionné sur **%{title}**. Il pourrait y avoir des détails pertinents ici : 34 | 35 | %{post_url} 36 | github_commits_populator: 37 | errors: 38 | repository_identifier_invalid_pm_title: "Action requise pour l'extension discourse-github" 39 | repository_identifier_invalid_pm: | 40 | Un dépôt spécifié dans l'extension discourse-github n'est pas valide : 41 | %{repo_name} 42 | 43 | Ils doivent être spécifiés en tant que « user/repo ». 44 | 45 | Les badges ne seront pas attribués tant que le nom n'aura pas été corrigé dans les paramètres du site 46 | 47 | et que « github_badges_enabled » ne sera pas réactivé. 48 | repository_not_found_pm_title: "Action requise pour l'extension discourse-github" 49 | repository_not_found_pm: | 50 | Un dépôt spécifié dans l'extension discourse-github a renvoyé une erreur 51 | %{repo_name} 52 | 53 | Les badges ne seront pas attribués tant que le nom n'aura pas été corrigé dans les 54 | paramètres du site discourse-github 55 | et que « github_badges_enabled » n'aura pas été réactivé. 56 | invalid_octokit_credentials_pm_title: "Action requise pour l'extension discourse-github" 57 | invalid_octokit_credentials_pm: "Les informations d'identification GitHub fournies à l'extension discourse-github ne sont pas valides. Le paramètre de site « badges github activés » \na été désactivé et les validations ne seront plus renseignées tant que le problème ne sera pas résolu.\n\nVérifiez le paramètre de site « jeton d'accès au lien github » pour vous assurer que le jeton est correct et autorisé à accéder à tous les\ndépôts via les\nparamètres de site discours-github.\n" 58 | -------------------------------------------------------------------------------- /config/locales/server.gl.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | gl: 8 | -------------------------------------------------------------------------------- /config/locales/server.he.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | he: 8 | site_settings: 9 | enable_discourse_github_plugin: "הפעלת התוסף discourse-github" 10 | github_linkback_enabled: "קישור תקלות ב־GitHub בחזרה לדיונים בפורום" 11 | github_linkback_projects: "רשימות של מיזמים לקשר מהם בחזרה" 12 | github_linkback_access_token: 'אסימון גישה תקף למשתמש שיפרסם את הקישור החוזה ולספירת הגשות/תרומות כדי להעניק עיטורים. ניתן לעיין כאן להנחיות איך לקבל את האסימון.' 13 | github_linkback_maximum_links: "מספר הקישורים החוזרים המרבי ליצירה מפוסט יחיד. כאשר פוסט מכיל יותר קישורים, לא נוצרים כלל." 14 | github_permalinks_enabled: "הפעלת שכתוב כתובות קבועות של GitHub" 15 | github_permalinks_exclude: "שם קובץ או תיקייה שיוחרגו משכתובי הקישורים הקבועים. יש לספק שם קובץ או נתיב מלא בלבד: user/repository/optional-directory/filename" 16 | github_badges_enabled: "הפעלת עיטורים של GitHub" 17 | github_badges_repos: "כתובות של מאגרי GitHub לסריקה אחר תרומות והגשות" 18 | github_silver_badge_min_commits: "מספר ההגשות המזערי לעיטור כסף" 19 | github_gold_badge_min_commits: "מספר ההגשות המזערי לעיטור זהב" 20 | errors: 21 | invalid_badge_repo: "עליך לספק כתובת GitHub או את שם המאגר בצורה github_user/repository_name" 22 | invalid_github_linkback_access_token: "עליך לספק אסימון גישה תקף לקישור חוזר אל GitHub שיש לו גישה למאגרי העיטורים שסיפקת." 23 | github_linkback: 24 | commit_template: | 25 | הגשה (Commit) זו הוזכרה ב־**%{title}**. יכול להיות שיש פרטים קשורים כאן: 26 | 27 | %{post_url} 28 | pr_template: | 29 | בקשת דחיפה (Pull Request) זו הוזכרה ב־**%{title}**. יכול להיות שיש פרטים קשורים כאן: 30 | 31 | %{post_url} 32 | issue_template: | 33 | נושא (Issue) זה הוזכר ב־**%{title}**. יכול להיות שיש פרטים קשורים כאן: 34 | 35 | %{post_url} 36 | github_commits_populator: 37 | errors: 38 | repository_identifier_invalid_pm_title: "נדרשת פעולה עבור תוסף discourse-github" 39 | repository_identifier_invalid_pm: | 40 | מאגר שצוין ב־discourse-github שגוי: 41 | %{repo_name} 42 | 43 | יש לציין אותם בתור `user/repo` (משתמש/מאגר). 44 | 45 | לא יוענקו עיטורים עד שהשם יתוקן 46 | בהגדרות האתר של discourse-github 47 | ו־„github_badges_enabled” (עיטורים של GitHub פעילים) מופעל בחזרה. 48 | repository_not_found_pm_title: "נדרשת פעולה עבור תוסף discourse-github" 49 | repository_not_found_pm: | 50 | מאגר שצוין ב־discourse-github החזירה שגיאת „לא נמצא”: %{repo_name} 51 | 52 | לא יוענקו עיטורים עד שהשם יתוקן 53 | בהגדרות האתר של discourse-github 54 | ו־„github_badges_enabled” (עיטורים של GitHub פעילים) מופעל בחזרה. 55 | invalid_octokit_credentials_pm_title: "נדרשת פעולה עבור תוסף discourse-github" 56 | invalid_octokit_credentials_pm: | 57 | פרטי הגישה ל־GitHub שסופקו לתוסף discourse-github שגויים. הגדרת האתר „github badges enabled” הושבתה וההגשות לא ימולאו עד לפתרון התקלה. 58 | 59 | כדאי לבדוק את הגדרת האתר „github linkback access token” (אסימון גישה לקישור חוזר ל־GitHub) כדי לוודא שהאסימון נכון ושיש ללו הרשאות לכל המאגרים דרך הגדרות האתר של discourse-github. 60 | -------------------------------------------------------------------------------- /config/locales/server.hr.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | hr: 8 | -------------------------------------------------------------------------------- /config/locales/server.hu.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | hu: 8 | -------------------------------------------------------------------------------- /config/locales/server.hy.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | hy: 8 | -------------------------------------------------------------------------------- /config/locales/server.id.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | id: 8 | -------------------------------------------------------------------------------- /config/locales/server.it.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | it: 8 | site_settings: 9 | enable_discourse_github_plugin: "Abilita il plugin discourse-github" 10 | github_linkback_enabled: "Collega i problemi di Github alle relative discussioni sul forum" 11 | github_linkback_projects: "Elenco dei progetti da collegare al forum" 12 | github_linkback_access_token: 'Un token di accesso valido per l''utente che pubblicherà il linkback e per il conteggio dei commit/contributi per la concessione di distintivi. Consulta qui le istruzioni per ottenere un token.' 13 | github_linkback_maximum_links: "Numero massimo di linkback da creare da un singolo messaggio. Quando un messaggio contiene più link, non ne viene creato nessuno." 14 | github_permalinks_enabled: "Abilita le sovrascritture dei collegamenti GitHub permanenti" 15 | github_permalinks_exclude: "Nome del file o directory che dovrebbe essere esclusa dalle sovrascritture dei collegamenti permanenti. Fornisci solo il nome del file o il percorso completo: user/repository/optional-directory/filename" 16 | github_badges_enabled: "Abilita distintivi GitHub" 17 | github_badges_repos: "URL degli archivi GitHub per cercare contributi e commit" 18 | github_silver_badge_min_commits: "Numero minimo di commit per il distintivo d'argento" 19 | github_gold_badge_min_commits: "Numero minimo di commit per il distintivo d'oro" 20 | errors: 21 | invalid_badge_repo: "Devi fornire un URL GitHub o il nome dell'archivio nel formato github_user/repository_name" 22 | invalid_github_linkback_access_token: "Devi fornire un token valido di accesso al linkback GitHub che permette di accedere agli archivi dei distintivi che hai fornito." 23 | github_linkback: 24 | commit_template: | 25 | Questo problema è stato menzionato in **%{title}**. Lì potresti trovare informazioni rilevanti: 26 | 27 | %{post_url} 28 | pr_template: | 29 | Questa richiesta pull è stata menzionata in **%{title}**. Lì potresti trovare informazioni rilevanti: 30 | 31 | %{post_url} 32 | issue_template: | 33 | Questo problema è stato menzionato in **%{title}**. Lì potresti trovare informazioni rilevanti: 34 | 35 | %{post_url} 36 | github_commits_populator: 37 | errors: 38 | repository_identifier_invalid_pm_title: "Azione richiesta per il plug-in discourse-github" 39 | repository_identifier_invalid_pm: | 40 | Un archivio indicato nel plugin discourse-github non è valido: 41 | %{repo_name} 42 | 43 | Dovrebbe essere indicato in formato `user/repo`. 44 | 45 | I distintivi non saranno assegnati fino a quando il nome non sarà corretto nelle 46 | Impostazioni del sito discourse-github 47 | e l'opzione "github_badges_enabled" non sarà riattivata. 48 | repository_not_found_pm_title: "Azione richiesta per il plug-in discourse-github" 49 | repository_not_found_pm: | 50 | Un archivio indicato nel plugin discourse-github ha restituito un errore "Non trovato": %{repo_name} 51 | 52 | I distintivi non saranno assegnati fino a quando il nome non sarà corretto nelle 53 | Impostazioni del sito discourse-github 54 | e l'opzione "github_badges_enabled" non sarà riattivata. 55 | invalid_octokit_credentials_pm_title: "Azione richiesta per il plug-in discourse-github" 56 | invalid_octokit_credentials_pm: | 57 | Le credenziali GitHub fornite al plug-in discourse-github non sono valide. L'impostazione del sito "github badge enabled" 58 | è stata disabilitata e i commit non verranno più compilati fino a quando il problema non sarà risolto. 59 | 60 | Controlla l'impostazione del sito "github linkback access token" per assicurarti che il token sia corretto e abbia l'autorizzazione per tutti gli 61 | accessi tramite le 62 | Impostazioni del sito discourse-github. 63 | -------------------------------------------------------------------------------- /config/locales/server.ja.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | ja: 8 | site_settings: 9 | enable_discourse_github_plugin: "discourse-github プラグインを有効にする" 10 | github_linkback_enabled: "GitHub の課題をフォーラムディスカッションにバックリンクする" 11 | github_linkback_projects: "バックリンクするプロジェクトのリスト" 12 | github_linkback_access_token: 'バックリンクを投稿するユーザー用とバッジを付与するためのコミット数/貢献数をカウントするための有効なアクセストークン。トークンの取得方法については、こちらをご覧ください。' 13 | github_linkback_maximum_links: "1 つの投稿から作成できるリンクバックの最大数。投稿に複数のリンクが含まれる場合は、何も作成されません。" 14 | github_permalinks_enabled: "GitHub パーマリンクの上書きを有効にする" 15 | github_permalinks_exclude: "パーマリンクの上書きから除外する必要のあるファイル名またはディレクトリ。ファイル名のみ、またはフルパス(user/repository/optional-directory/filename)を指定します" 16 | github_badges_enabled: "GitHub バッジを有効にする" 17 | github_badges_repos: "貢献とコミットをスキャンする GitHub リポジトリの URL" 18 | github_silver_badge_min_commits: "シルバーバッジの最低コミット数" 19 | github_gold_badge_min_commits: "ゴールドバッジの最低コミット数" 20 | errors: 21 | invalid_badge_repo: "GitHub URL または github_user/repository_name の形式のリポジトリ名を指定する必要があります" 22 | invalid_github_linkback_access_token: "指定したバッジリポジトリにアクセスできる有効な GitHub バックリンクアクセストークンを指定する必要があります。" 23 | github_linkback: 24 | commit_template: | 25 | このコミットは **%{title}** でメンションされています。関連する詳細があるかもしれません。 26 | 27 | %{post_url} 28 | pr_template: | 29 | このプルリクエストは **%{title}** でメンションされています。関連する詳細があるかもしれません。 30 | 31 | %{post_url} 32 | issue_template: | 33 | この課題は **%{title}** でメンションされています。関連する詳細があるかもしれません。 34 | 35 | %{post_url} 36 | github_commits_populator: 37 | errors: 38 | repository_identifier_invalid_pm_title: "discourse-github プラグインにアクションが必要です" 39 | repository_identifier_invalid_pm: | 40 | discourse-github プラグインに指定されたリポジトリは無効です: 41 | %{repo_name} 42 | 43 | `user/repo` の形式で指定されている必要があります。 44 | 45 | この名前が 46 | discourse-github のサイト設定で修正され、"github_badges_enabled" がもう一度オンになるまで、バッジは授与されません。 47 | repository_not_found_pm_title: "discourse-github プラグインにアクションが必要です" 48 | repository_not_found_pm: | 49 | discourse-github プラグインに指定されたリポジトリから "見つかりません" 50 | エラーが返されました: %{repo_name} 51 | 52 | この名前が 53 | discourse-github のサイト設定で修正され、"github_badges_enabled" がもう一度オンになるまで、バッジは授与されません。 54 | invalid_octokit_credentials_pm_title: "discourse-github プラグインにアクションが必要です" 55 | invalid_octokit_credentials_pm: | 56 | discourse-github プラグインに指定された GitHub 資格情報は無効です。"github badges enabled\ サイト設定が無効になっているため、この問題が解決するまでコミットは表示されません。 57 | 58 | discourse-github サイト設定から "GitHub バックリンクアクセストークン" サイト設定を確認し、トークンが正しく、すべてのリポジトリへの権限が付与されていることを確認してください。 59 | -------------------------------------------------------------------------------- /config/locales/server.ko.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | ko: 8 | -------------------------------------------------------------------------------- /config/locales/server.lt.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | lt: 8 | -------------------------------------------------------------------------------- /config/locales/server.lv.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | lv: 8 | -------------------------------------------------------------------------------- /config/locales/server.nb_NO.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | nb_NO: 8 | -------------------------------------------------------------------------------- /config/locales/server.nl.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | nl: 8 | site_settings: 9 | enable_discourse_github_plugin: "Discourse-Github-plug-in inschakelen" 10 | github_linkback_enabled: "GitHub-problemen teruglinken naar forumdiscussies" 11 | github_linkback_projects: "Lijst van projecten om van terug te linken" 12 | github_linkback_access_token: 'Een geldig toegangstoken voor de gebruiker die de teruglink plaatst en voor het tellen van commits/bijdragen om badges toe te kennen. Zie hier voor instructies voor het verkrijgen van een token.' 13 | github_linkback_maximum_links: "Maximaal aantal teruglinks om te maken vanuit één enkele post. Als een bericht meer links bevat, wordt er geen gemaakt." 14 | github_permalinks_enabled: "Overschrijven van GitHub-permalink inschakelen" 15 | github_permalinks_exclude: "Bestandsnaam of map die moet worden uitgesloten van permalinkoverschrijvingen. Geef alleen de bestandsnaam of het volledige pad op: gebruiker/repository/optionele-directory/bestandsnaam" 16 | github_badges_enabled: "GitHub-badges inschakelen" 17 | github_badges_repos: "URL's van de GitHub-repository's om te scannen op bijdragen en commits" 18 | github_silver_badge_min_commits: "Minimaal aantal commits voor zilveren badge" 19 | github_gold_badge_min_commits: "Minimaal aantal commits voor gouden badge" 20 | errors: 21 | invalid_badge_repo: "Je moet een GitHub-URL of de repositorynaam opgeven in de vorm githubgebruiker/repositorynaam" 22 | invalid_github_linkback_access_token: "Je moet een geldig GitHub-linkbackaccesstoken opgeven dat toegang heeft tot de door jou opgegeven badgerepository's." 23 | github_linkback: 24 | commit_template: | 25 | Deze commit is vermeld op **%{title}**. Er kunnen relevante details zijn: 26 | 27 | %{post_url} 28 | pr_template: | 29 | Dit pullverzoek is vermeld op **%{title}**. Er kunnen relevante details zijn: 30 | 31 | %{post_url} 32 | issue_template: | 33 | Dit probleem is vermeld op **%{title}**. Er kunnen relevante details zijn: 34 | 35 | %{post_url} 36 | github_commits_populator: 37 | errors: 38 | repository_identifier_invalid_pm_title: "Actie vereist voor Discourse-Github-plug-in" 39 | repository_identifier_invalid_pm: | 40 | Een repository opgegeven in de Discourse-Github-plug-in is ongeldig: 41 | %{repo_name} 42 | 43 | Deze moet worden opgegeven als 'gebruiker/respository'. 44 | 45 | Badges worden pas toegekend als de naam is gecorrigeerd in 46 | Discourse-Github-site-instellingen 47 | en 'github_badges_enabled' weer is ingeschakeld. 48 | repository_not_found_pm_title: "Actie vereist voor Discourse-Github-plug-in" 49 | repository_not_found_pm: | 50 | Een repository opgegeven in de Discourse-Github-plug-in heeft een 'Niet gevonden'-fout 51 | geretourneerd: %{repo_name} 52 | 53 | Badges worden pas toegekend als de naam is gecorrigeerd in 54 | Discourse-Github-site-instellingen 55 | en 'github_badges_enabled' weer is ingeschakeld. 56 | invalid_octokit_credentials_pm_title: "Actie vereist voor Discourse-Github-plug-in" 57 | invalid_octokit_credentials_pm: | 58 | De GitHub-aanmeldgegevens die aan de Discourse-Github-plug-in zijn verstrekt, zijn ongeldig. De site-instelling 'github badges enabled' is uitgeschakeld en commits worden niet meer ingevuld totdat het probleem is opgelost. 59 | 60 | Controleer de site-instelling 'github linkback access token' om zeker te zijn dat het token juist is en toestemming heeft voor alle repository's via 61 | Discourse-Github-site-instellingen. 62 | -------------------------------------------------------------------------------- /config/locales/server.pl_PL.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | pl_PL: 8 | site_settings: 9 | enable_discourse_github_plugin: "Włącz wtyczkę discourse-github" 10 | github_linkback_enabled: "Połącz problemy z GitHub z dyskusjami na forum" 11 | github_linkback_projects: "Lista projektów do odsyłania" 12 | github_linkback_access_token: 'Ważny token dostępu dla użytkownika, który opublikuje link zwrotny oraz do liczenia commitów/wkładów w celu przyznania odznak. Zerknij tutaj, aby uzyskać instrukcje, jak zdobyć token.' 13 | github_badges_enabled: "Włącz odznaki GitHub" 14 | -------------------------------------------------------------------------------- /config/locales/server.pt.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | pt: 8 | -------------------------------------------------------------------------------- /config/locales/server.pt_BR.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | pt_BR: 8 | site_settings: 9 | enable_discourse_github_plugin: "Ativar o plugin discourse-github" 10 | github_linkback_enabled: "O link do Github envia de volta para discussões do fórum" 11 | github_linkback_projects: "Lista de projetos para fazer linkback" 12 | github_linkback_access_token: 'Um token de acesso válido para o(a) usuário(a) que postar o linkback e contar confirmações/contribuições para conceder emblemas. Acesse aqui para obter instruções de como receber um token.' 13 | github_linkback_maximum_links: "Número máximo de linkbacks para criar a partir de uma única postagem. Quando uma postagem contiver mais links, nenhum será criado." 14 | github_permalinks_enabled: "Ativar substituições de permalink do GitHub" 15 | github_permalinks_exclude: "Diretório ou nome do arquivo que deve ser excluído das substituições de permalink. Informe apenas o nome do arquivo ou caminho completo: user/repository/optional-directory/filename" 16 | github_badges_enabled: "Ativar emblemas do GitHub" 17 | github_badges_repos: "URLs do repositório do GitHub para verificar contribuições e confirmações" 18 | github_silver_badge_min_commits: "Número mínimo de confirmações para emblema de prata" 19 | github_gold_badge_min_commits: "Número mínimo de confirmações para emblema de ouro" 20 | errors: 21 | invalid_badge_repo: "Você deve informar uma URL do GitHub ou o nome do repositório no formato github_user/repository_name" 22 | invalid_github_linkback_access_token: "Você deve informar um token válido de acesso ao linkback do GitHub, com acesso aos repositórios do emblema informados." 23 | github_linkback: 24 | commit_template: | 25 | A confirmação foi mencionada em **%{title}**. Pode haver detalhes importantes: 26 | 27 | %{post_url} 28 | pr_template: | 29 | O pedido de recebimento foi mencionado em **%{title}**. Pode haver detalhes importantes: 30 | 31 | %{post_url} 32 | issue_template: | 33 | Este problema foi mencionado em **%{title}**. Pode haver detalhes importantes: 34 | 35 | %{post_url} 36 | github_commits_populator: 37 | errors: 38 | repository_identifier_invalid_pm_title: "Ação necessária para o plugin discourse-github" 39 | repository_identifier_invalid_pm: | 40 | Um repositório especificado no plugin discourse-github não é válido: 41 | %{repo_name} 42 | 43 | Deve ser especificado como "user/repo". 44 | 45 | Emblemas não serão concedidos até que o nome seja corrigido 46 | Configurações do site do discourse-github 47 | e "github_badges_enabled" seja reativado. 48 | repository_not_found_pm_title: "Ação necessária para o plugin discourse-github" 49 | repository_not_found_pm: | 50 | Um repositório especificado no plugin discourse-github retornou um "Não encontrado" 51 | %{repo_name} 52 | 53 | Emblemas não serão concedidos até que o nome seja corrigido 54 | Configurações do site do discourse-github 55 | e "github_badges_enabled" seja reativado. 56 | invalid_octokit_credentials_pm_title: "Ação necessária para o plugin discourse-github" 57 | invalid_octokit_credentials_pm: | 58 | As credenciais do GitHub informadas ao plugin discourse-github não são válidas. A configuração "github badges enabled\site" 59 | foi desativada, e as confirmações não serão mais preenchidas até que o problema seja solucionado. 60 | 61 | Confira a configuração do site "github linkback access token" para garantir que o token está correto e tem permissão para todos os respositórios via 62 | discourse-github Site Settings. 63 | -------------------------------------------------------------------------------- /config/locales/server.ro.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | ro: 8 | -------------------------------------------------------------------------------- /config/locales/server.ru.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | ru: 8 | site_settings: 9 | enable_discourse_github_plugin: "Включить плагин discourse-github" 10 | github_linkback_enabled: "Связать тикеты GitHub с обсуждениями на форуме" 11 | github_linkback_projects: "Список проектов, на которые можно ссылаться" 12 | github_linkback_access_token: 'Действующий токен доступа для пользователя, который будет размещать обратную ссылку и для подсчёта коммитов/вкладов при присвоении наград. Получение токена описано в этой теме.' 13 | github_linkback_maximum_links: "Максимальное количество обратных ссылок, создаваемых из одного сообщения. Если сообщение содержит больше ссылок - ни одна не создается." 14 | github_permalinks_enabled: "Включить перезапись постоянных ссылок GitHub" 15 | github_permalinks_exclude: "Имя файла или каталога, которые следует исключить из перезаписи постоянных ссылок. Укажите только имя файла или полный путь: пользователь/репозиторий/необязательный каталог/имя файла" 16 | github_badges_enabled: "Включить награды GitHub" 17 | github_badges_repos: "URL-адреса репозиториев GitHub для проверки вкладов и коммитов" 18 | github_silver_badge_min_commits: "Минимальное количество коммитов для получения серебряного значка" 19 | github_gold_badge_min_commits: "Минимальное количество коммитов для получения золотого значка" 20 | errors: 21 | invalid_badge_repo: "Вы должны предоставить URL GitHub или имя репозитория в формате 'github_пользователь/название_репозитория'" 22 | invalid_github_linkback_access_token: "Необходимо указать действующий токен доступа к GitHub Linkback, который имеет доступ к указанным вами репозиториям с наградами." 23 | github_linkback: 24 | commit_template: | 25 | Этот коммит был упомянут в **%{title}**. Там могут быть соответствующие подробности: 26 | 27 | %{post_url} 28 | pr_template: | 29 | Этот запрос на слияние был упомянут в **%{title}**. Там могут быть соответствующие подробности: 30 | 31 | %{post_url} 32 | issue_template: | 33 | Эта проблема была упомянута в **%{title}**. Там могут быть соответствующие подробности: 34 | 35 | %{post_url} 36 | github_commits_populator: 37 | errors: 38 | repository_identifier_invalid_pm_title: "Плагин «discourse-github» требует принятия мер" 39 | repository_identifier_invalid_pm: | 40 | Репозиторий, указанный в плагине «discourse-github», недействителен: 41 | %{repo_name} 42 | 43 | Он должен быть указан как `пользователь/репозиторий`. 44 | 45 | Награды не будут выдаваться до тех пор, пока название не будет исправлено в 46 | настройках «discourse-github» 47 | и параметр «github_badges_enabled» не будет снова включён. 48 | repository_not_found_pm_title: "Плагин «discourse-github» требует принятия мер" 49 | repository_not_found_pm: | 50 | Репозиторий, указанный в плагине discourse-github, выдаёт ошибку «Не найдено»: %{repo_name} 51 | 52 | Награды не будут выдаваться до тех пор, пока название не будет исправлено в 53 | настройках «discourse-github» 54 | и параметр «github_badges_enabled» не будет снова включён. 55 | invalid_octokit_credentials_pm_title: "Плагин «discourse-github» требует принятия мер" 56 | invalid_octokit_credentials_pm: | 57 | Учётные данные GitHub, предоставленные плагину «discourse-github», недействительны. Награды Github отключены, коммиты не будут заполняться, пока проблема не будет решена. 58 | 59 | Проверьте настройку токена доступа Github Linkback: токен должен быть действителен и должен иметь право доступа ко всем 60 | репозиториям — см. 61 | настройки «discourse-github». 62 | -------------------------------------------------------------------------------- /config/locales/server.sk.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | sk: 8 | -------------------------------------------------------------------------------- /config/locales/server.sl.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | sl: 8 | -------------------------------------------------------------------------------- /config/locales/server.sq.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | sq: 8 | -------------------------------------------------------------------------------- /config/locales/server.sr.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | sr: 8 | -------------------------------------------------------------------------------- /config/locales/server.sv.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | sv: 8 | site_settings: 9 | enable_discourse_github_plugin: "Aktivera insticksprogrammet Discourse-github" 10 | github_linkback_enabled: "Länka GitHub-ärenden tillbaka till forumdiskussioner" 11 | github_linkback_projects: "Lista över projekt att länka tillbaka från" 12 | github_linkback_access_token: 'En giltig åtkomsttoken för användaren som lägger upp återlänken samt för att räkna åtaganden/bidrag för att ge märken. Se här för instruktioner om hur du skaffar en token.' 13 | github_linkback_maximum_links: "Maximalt antal återlänkar att skapa från ett enda inlägg. När ett inlägg innehåller fler länkar skapas inga." 14 | github_permalinks_enabled: "Aktivera överskrivning av GitHub-permalänk" 15 | github_permalinks_exclude: "Filnamn eller katalog som bör uteslutas från permalänksöverskrivningar. Ange endast filnamn eller fullständig sökväg: user/repository/optional-directory/filname" 16 | github_badges_enabled: "Aktivera GitHub-märken" 17 | github_badges_repos: "Webbadresser till GitHub-depåer för att söka efter bidrag och åtaganden" 18 | github_silver_badge_min_commits: "Minsta antal åtaganden för silvermärke" 19 | github_gold_badge_min_commits: "Minsta antal åtaganden för guldmärke" 20 | errors: 21 | invalid_badge_repo: "Du måste ange en GitHub-URL eller databasnamnet i formatet github_user/repository_name" 22 | invalid_github_linkback_access_token: "Du måste ange en giltig GitHub återkopplingsbehörighetstoken som har åtkomst till de märkesdatabaser som du har angett." 23 | github_linkback: 24 | commit_template: | 25 | Detta åtagande har nämnts i **%{title}**. Det kan finnas relevanta detaljer där: 26 | 27 | %{post_url} 28 | pr_template: | 29 | Denna dragbegäran har nämnts i **%{title}**. Det kan finnas relevanta detaljer där: 30 | 31 | %{post_url} 32 | issue_template: | 33 | Detta ärende har nämnts i **%{title}**. Det kan finnas relevanta detaljer där: 34 | 35 | %{post_url} 36 | github_commits_populator: 37 | errors: 38 | repository_identifier_invalid_pm_title: "Åtgärd krävs för insticksprogrammet discourse-github" 39 | repository_identifier_invalid_pm: | 40 | En databas som anges i insticksprogrammet Discourse-github är ogiltig: 41 | %{repo_name} 42 | 43 | De bör anges som `user/repo`. 44 | 45 | Märken tilldelas inte förrän namnet har korrigerats i 46 | discourse-github webbplatsinställningar 47 | och ”github_badges_enabled” är aktiverat igen. 48 | repository_not_found_pm_title: "Åtgärd krävs för insticksprogrammet discourse-github" 49 | repository_not_found_pm: | 50 | En databas som anges i insticksprogrammet Discourse-github returnerade felet ”Hittas inte” 51 | : %{repo_name} 52 | 53 | Märken kommer inte att tilldelas förrän namnet har korrigerats i 54 | discourse-github webbplatsinställningar 55 | och ”github_badges_enabled” är aktiverat igen. 56 | invalid_octokit_credentials_pm_title: "Åtgärd krävs för insticksprogrammet discourse-github" 57 | invalid_octokit_credentials_pm: | 58 | GitHubs autentiseringsuppgifter som angivits till insticksprogrammet discourse-github är ogiltiga. Inställningen ”github märken aktiverad" 59 | har inaktiverats, och åtaganden kommer inte längre att fyllas i förrän problemet är löst. 60 | 61 | Kontrollera inställningen ”github linkback access token” för att säkerställa att token är korrekt och har behörighet till alla 62 | depåer via 63 | discourse-github webbplatsinställningar. 64 | -------------------------------------------------------------------------------- /config/locales/server.sw.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | sw: 8 | -------------------------------------------------------------------------------- /config/locales/server.te.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | te: 8 | -------------------------------------------------------------------------------- /config/locales/server.th.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | th: 8 | -------------------------------------------------------------------------------- /config/locales/server.tr_TR.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | tr_TR: 8 | site_settings: 9 | enable_discourse_github_plugin: "discourse-github eklentisini etkinleştirin" 10 | github_linkback_enabled: "GitHub sorunlarını forum tartışmalarına geri bağlayın" 11 | github_linkback_projects: "Geri bağlantı kurulacak projelerin listesi" 12 | github_linkback_access_token: 'Geri bağlantıyı gönderecek kullanıcı için ve rozet vermek üzere taahhütleri/katkıları saymak için geçerli bir erişim token''ı. Nasıl token alınacağına ilişkin talimatlar için burayı inceleyin.' 13 | github_linkback_maximum_links: "Tek bir gönderiden oluşturulacak maksimum geri bağlantı sayısı. Bir gönderi daha fazla bağlantı içeriyorsa hiçbiri oluşturulmaz." 14 | github_permalinks_enabled: "GitHub kalıcı bağlantılarının üzerine yazmayı etkinleştirin" 15 | github_permalinks_exclude: "Kalıcı bağlantı üzerine yazma işlemlerinin dışında bırakılması gereken dosya adı veya dizin. Yalnızca dosya adı veya tam yol sağlayın: kullanıcı/havuz/isteğe bağlı dizin/dosya adı" 16 | github_badges_enabled: "GitHub rozetlerini etkinleştirin" 17 | github_badges_repos: "Katkılar ve taahhütler için taranacak GitHub depolarının URL'leri" 18 | github_silver_badge_min_commits: "Gümüş rozet için minimum taahhüt sayısı" 19 | github_gold_badge_min_commits: "Altın rozet için minimum taahhüt sayısı" 20 | errors: 21 | invalid_badge_repo: "Bir GitHub URL'si veya depo adını github_user/repository_name biçiminde sağlamalısınız" 22 | invalid_github_linkback_access_token: "Sağladığınız rozet depolarına erişimi olan geçerli bir GitHub linkback erişim token'ı sağlamalısınız." 23 | github_linkback: 24 | commit_template: | 25 | Bu taahhütten **%{title}** başlığında bahsedildi. Orada ilgili ayrıntılar olabilir: 26 | 27 | %{post_url} 28 | pr_template: | 29 | Bu çekme talebinden **%{title}** başlığında bahsedildi. Orada ilgili ayrıntılar olabilir: 30 | 31 | %{post_url} 32 | issue_template: | 33 | Bu sorundan **%{title}** başlığında bahsedildi. Orada ilgili ayrıntılar olabilir: 34 | 35 | %{post_url} 36 | github_commits_populator: 37 | errors: 38 | repository_identifier_invalid_pm_title: "discourse-github eklentisi için eylem gerekli" 39 | repository_identifier_invalid_pm: | 40 | discourse-github eklentisinde belirtilen bir depo geçersiz: 41 | %{repo_name} 42 | 43 | "user/repo" olarak belirtilmeliler. 44 | 45 | İsim 46 | discourse-github Site Ayarları 47 | adresinde düzeltilene ve "github_badges_enabled" tekrar açılana kadar rozet verilmeyecek. 48 | repository_not_found_pm_title: "discourse-github eklentisi için eylem gerekli" 49 | repository_not_found_pm: | 50 | discourse-github eklentisinde belirtilen bir depo "Bulunamadı" 51 | hatası döndürdü: %{repo_name} 52 | 53 | İsim 54 | discourse-github Site Ayarları 55 | içinde düzeltilene ve "github_badges_enabled" tekrar açılana kadar rozet verilmeyecek. 56 | invalid_octokit_credentials_pm_title: "discourse-github eklentisi için eylem gerekli" 57 | invalid_octokit_credentials_pm: | 58 | discourse-github eklentisine eklenen GitHub kimlik bilgileri geçersiz. "github rozetleri etkin\ site 59 | ayarı devre dışı ve sorun çözülene kadar taahhütler doldurulmayacak. 60 | 61 | Belirtecin doğru olduğundan ve 62 | discourse-github Site Ayarları aracılığıyla tüm 63 | depolara izne sahip olduğundan emin olmak için "github geri bağlantı erişim token'ı" site ayarınızı kontrol edin. 64 | -------------------------------------------------------------------------------- /config/locales/server.ug.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | ug: 8 | -------------------------------------------------------------------------------- /config/locales/server.uk.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | uk: 8 | -------------------------------------------------------------------------------- /config/locales/server.ur.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | ur: 8 | -------------------------------------------------------------------------------- /config/locales/server.vi.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | vi: 8 | -------------------------------------------------------------------------------- /config/locales/server.zh_CN.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | zh_CN: 8 | site_settings: 9 | enable_discourse_github_plugin: "启用 discourse-github 插件" 10 | github_linkback_enabled: "将 GitHub 议题链接回论坛讨论" 11 | github_linkback_projects: "待链接回的项目列表" 12 | github_linkback_access_token: '一个有效访问令牌,用于将发布反向链接的用户以及计算提交/贡献以授予徽章。有关如何获取令牌的说明,请参阅此处。' 13 | github_linkback_maximum_links: "从单个帖子创建的最大反向链接数。当帖子包含更多链接时,将不会创建任何链接。" 14 | github_permalinks_enabled: "启用 GitHub 永久链接覆盖" 15 | github_permalinks_exclude: "应从永久链接覆盖中排除的文件名或目录。仅提供文件名或完整路径:user/repository/optional-directory/filename" 16 | github_badges_enabled: "启用 GitHub 徽章" 17 | github_badges_repos: "用于扫描贡献和提交的 GitHub 仓库的 URL" 18 | github_silver_badge_min_commits: "白银徽章的最低提交次数" 19 | github_gold_badge_min_commits: "黄金徽章的最低提交次数" 20 | errors: 21 | invalid_badge_repo: "您必须提供 GitHub URL 或格式为 github_user/repository_name 的仓库名称" 22 | invalid_github_linkback_access_token: "您必须提供有效的 GitHub 反向链接访问令牌,该令牌应该可以访问您提供的徽章仓库。" 23 | github_linkback: 24 | commit_template: | 25 | 此提交已在 **%{title}** 上被提及。那里可能有相关详细信息: 26 | 27 | %{post_url} 28 | pr_template: | 29 | 此拉取请求已在 **%{title}** 上被提及。那里可能有相关详细信息: 30 | 31 | %{post_url} 32 | issue_template: | 33 | 此问题已在 **%{title}** 上被提及。那里可能有相关详细信息: 34 | 35 | %{post_url} 36 | github_commits_populator: 37 | errors: 38 | repository_identifier_invalid_pm_title: "discourse-github 插件所需操作" 39 | repository_identifier_invalid_pm: | 40 | discourse-github 插件中指定的仓库无效: 41 | %{repo_name} 42 | 43 | 它们应该被指定为 `user/repo`。 44 | 45 | 除非在 discourse-github 站点设置中更正名称并重新打开“github_badges_enabled”,否则不会授予徽章。 46 | repository_not_found_pm_title: "discourse-github 插件所需操作" 47 | repository_not_found_pm: | 48 | discourse-github 插件中指定的仓库返回了“找不到”错误:%{repo_name} 49 | 50 | 除非在 discourse-github 站点设置中更正名称并重新打开“github_badges_enabled”,否则不会授予徽章。 51 | invalid_octokit_credentials_pm_title: "discourse-github 插件所需操作" 52 | invalid_octokit_credentials_pm: | 53 | 提供给 discourse-github 插件的 GitHub 凭据无效。“github badges enabled”站点设置已被禁用,并且在议题解决之前将不会填充提交。 54 | 55 | 检查您的“github 反向链接访问令牌”站点设置,确保令牌正确并通过 discourse-github 站点设置获得对所有仓库的权限。 56 | -------------------------------------------------------------------------------- /config/locales/server.zh_TW.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | zh_TW: 8 | -------------------------------------------------------------------------------- /config/settings.yml: -------------------------------------------------------------------------------- 1 | discourse_github: 2 | enable_discourse_github_plugin: true 3 | github_linkback_enabled: 4 | default: false 5 | github_linkback_projects: 6 | default: "" 7 | type: list 8 | github_linkback_access_token: 9 | default: "" 10 | secret: true 11 | validator: "GithubLinkbackAccessTokenSettingValidator" 12 | github_linkback_maximum_links: 13 | default: 25 14 | min: 1 15 | github_badges_enabled: 16 | default: false 17 | github_badges_repos: 18 | default: "" 19 | type: list 20 | validator: "GithubBadgesRepoSettingValidator" 21 | github_permalinks_enabled: 22 | default: false 23 | github_permalinks_exclude: 24 | default: "README.md" 25 | type: list 26 | github_silver_badge_min_commits: 27 | default: 25 28 | min: 1 29 | github_gold_badge_min_commits: 30 | default: 250 31 | min: 1 32 | -------------------------------------------------------------------------------- /db/migrate/20190617035051_rename_site_setting_github_badges_repo.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class RenameSiteSettingGithubBadgesRepo < ActiveRecord::Migration[5.2] 4 | def up 5 | execute(<<~SQL) 6 | UPDATE site_settings SET name = 'github_badges_repos', data_type = 8 WHERE name = 'github_badges_repo' AND data_type = 1 7 | SQL 8 | end 9 | 10 | def down 11 | execute(<<~SQL) 12 | UPDATE site_settings SET name = 'github_badges_repo', data_type = 1 WHERE name = 'github_badges_repos' AND data_type = 8 13 | SQL 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20190618174229_create_github_repos.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateGithubRepos < ActiveRecord::Migration[5.2] 4 | def change 5 | create_table :github_repos do |t| 6 | t.string :name, null: false, limit: 255 7 | t.timestamps null: false 8 | end 9 | add_index :github_repos, :name, unique: true 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20190618183340_create_github_commits.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateGithubCommits < ActiveRecord::Migration[5.2] 4 | def change 5 | create_table :github_commits do |t| 6 | t.references :repo, null: false # rubocop:disable Discourse/NoAddReferenceOrAliasesActiveRecordMigration 7 | t.string :sha, limit: 40, null: false 8 | t.string :email, limit: 513, null: false 9 | t.timestamp :committed_at, null: false 10 | t.integer :role_id, null: false 11 | t.boolean :merge_commit, null: false, default: false 12 | t.timestamps null: false 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20201210032852_discourse_github_rebuild_git_history.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class DiscourseGithubRebuildGitHistory < ActiveRecord::Migration[6.0] 4 | def up 5 | # the table will be repopulated the next time the `UpdateJob` runs. 6 | # This will not have any noticeable effects on the site because 7 | # this table is merely used to grant the committer and contributor 8 | # badges. Deleting commits will not cause users to lose badges. 9 | execute "DELETE FROM github_commits" 10 | end 11 | 12 | def down 13 | raise ActiveRecord::IrreversibleMigration 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20230227050148_update_github_badge_icons.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class UpdateGithubBadgeIcons < ActiveRecord::Migration[7.0] 4 | def change 5 | badges = [ 6 | DiscourseGithubPlugin::GithubBadges::BADGE_NAME_BRONZE, 7 | DiscourseGithubPlugin::GithubBadges::BADGE_NAME_SILVER, 8 | DiscourseGithubPlugin::GithubBadges::BADGE_NAME_GOLD, 9 | DiscourseGithubPlugin::GithubBadges::COMMITTER_BADGE_NAME_BRONZE, 10 | DiscourseGithubPlugin::GithubBadges::COMMITTER_BADGE_NAME_SILVER, 11 | DiscourseGithubPlugin::GithubBadges::COMMITTER_BADGE_NAME_GOLD, 12 | ] 13 | execute <<~SQL 14 | UPDATE badges 15 | SET icon = 'fab-git-alt' 16 | WHERE 17 | name IN (#{badges.map { |b| "'#{b}'" }.join(",")}) 18 | AND icon = 'fa-certificate' 19 | SQL 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import DiscourseRecommended from "@discourse/lint-configs/eslint"; 2 | 3 | export default [...DiscourseRecommended]; 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "devDependencies": { 4 | "@discourse/lint-configs": "2.21.0", 5 | "ember-template-lint": "7.7.0", 6 | "eslint": "9.27.0", 7 | "prettier": "3.5.3", 8 | "stylelint": "16.19.1" 9 | }, 10 | "engines": { 11 | "node": ">= 22", 12 | "npm": "please-use-pnpm", 13 | "yarn": "please-use-pnpm", 14 | "pnpm": "9.x" 15 | }, 16 | "packageManager": "pnpm@9.15.5" 17 | } 18 | -------------------------------------------------------------------------------- /plugin.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # name: discourse-github 4 | # about: Allows staff to assign badges to users based on GitHub contributions, and allows users to create Github Linkbacks and Permalinks 5 | # meta_topic_id: 99895 6 | # version: 0.3 7 | # authors: Robin Ward, Sam Saffron 8 | # url: https://github.com/discourse/discourse-github 9 | 10 | gem "sawyer", "0.9.2" 11 | gem "octokit", "5.6.1" 12 | 13 | # Site setting validators must be loaded before initialize 14 | require_relative "app/lib/github_badges_repo_setting_validator.rb" 15 | require_relative "app/lib/github_linkback_access_token_setting_validator.rb" 16 | 17 | enabled_site_setting :enable_discourse_github_plugin 18 | 19 | after_initialize do 20 | %w[ 21 | ../app/models/github_commit.rb 22 | ../app/models/github_repo.rb 23 | ../app/lib/github_linkback.rb 24 | ../app/lib/github_badges.rb 25 | ../app/lib/github_permalinks.rb 26 | ../app/lib/commits_populator.rb 27 | ../app/jobs/regular/create_github_linkback.rb 28 | ../app/jobs/scheduled/grant_github_badges.rb 29 | ../app/jobs/regular/replace_github_non_permalinks.rb 30 | ].each { |path| require File.expand_path(path, __FILE__) } 31 | 32 | on(:post_created) do |post| 33 | if SiteSetting.github_linkback_enabled? && SiteSetting.enable_discourse_github_plugin? 34 | GithubLinkback.new(post).enqueue 35 | end 36 | end 37 | 38 | on(:post_edited) do |post| 39 | if SiteSetting.github_linkback_enabled? && SiteSetting.enable_discourse_github_plugin? 40 | GithubLinkback.new(post).enqueue 41 | end 42 | end 43 | 44 | on(:before_post_process_cooked) do |doc, post| 45 | if SiteSetting.github_permalinks_enabled? && SiteSetting.enable_discourse_github_plugin? 46 | GithubPermalinks.replace_github_non_permalinks(post) 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/jobs/create_github_linkback_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rails_helper" 4 | 5 | describe Jobs::CreateGithubLinkback do 6 | before { SiteSetting.github_linkback_enabled = true } 7 | 8 | it "shouldn't raise error if post not found" do 9 | post = Fabricate(:post) 10 | post.destroy! 11 | expect { Jobs::CreateGithubLinkback.new.execute(post_id: post.id) }.not_to raise_error 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/jobs/replace_github_non_permalinks_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rails_helper" 4 | 5 | describe Jobs::ReplaceGithubNonPermalinks do 6 | let(:job) { described_class.new } 7 | let(:github_url) do 8 | "https://github.com/test/onebox/blob/master/lib/onebox/engine/github_blob_onebox.rb" 9 | end 10 | let(:github_permanent_url) do 11 | "https://github.com/test/onebox/blob/815ea9c0a8ffebe7bd7fcd34c10ff28c7a6b6974/lib/onebox/engine/github_blob_onebox.rb" 12 | end 13 | let(:github_url2) { "https://github.com/test/discourse/blob/master/app/models/tag.rb#L1-L3" } 14 | let(:github_permanent_url2) do 15 | "https://github.com/test/discourse/blob/7e4edcfae8a3c0e664b836ee7c5f28b47853a2f8/app/models/tag.rb#L1-L3" 16 | end 17 | let(:broken_github_url) do 18 | "https://github.com/test/oneblob/blob/master/lib/onebox/engine/nonexistent.rb" 19 | end 20 | let(:github_response_body) { { sha: "815ea9c0a8ffebe7bd7fcd34c10ff28c7a6b6974", commit: {} } } 21 | let(:github_response_body2) { { sha: "7e4edcfae8a3c0e664b836ee7c5f28b47853a2f8", commit: {} } } 22 | 23 | before do 24 | stub_request(:get, "https://api.github.com/repos/test/onebox/commits/master").to_return( 25 | status: 200, 26 | body: github_response_body.to_json, 27 | headers: { 28 | }, 29 | ) 30 | stub_request( 31 | :get, 32 | "https://api.github.com/repos/test/onebox/commits/815ea9c0a8ffebe7bd7fcd34c10ff28c7a6b6974", 33 | ).to_return(status: 200, body: github_response_body.to_json, headers: {}) 34 | stub_request(:get, "https://api.github.com/repos/test/oneblob/commits/master").to_return( 35 | status: 404, 36 | ) 37 | stub_request(:get, "https://api.github.com/repos/test/discourse/commits/master").to_return( 38 | status: 200, 39 | body: github_response_body2.to_json, 40 | headers: { 41 | }, 42 | ) 43 | end 44 | 45 | describe "#execute" do 46 | before do 47 | Jobs.run_immediately! 48 | SiteSetting.github_permalinks_enabled = true 49 | end 50 | 51 | it "replaces link with permanent link" do 52 | stub_request(:head, github_permanent_url).to_return(status: 200, body: "", headers: {}) 53 | stub_request( 54 | :get, 55 | "https://raw.githubusercontent.com/test/onebox/815ea9c0a8ffebe7bd7fcd34c10ff28c7a6b6974/lib/onebox/engine/github_blob_onebox.rb", 56 | ).to_return(status: 200, body: "", headers: {}) 57 | 58 | post = Fabricate(:post, raw: github_url) 59 | job.execute(post_id: post.id) 60 | post.reload 61 | 62 | expect(post.raw).to eq(github_permanent_url) 63 | end 64 | 65 | it "doesn't replace the link if it's already permanent" do 66 | post = Fabricate(:post, raw: github_permanent_url) 67 | job.execute(post_id: post.id) 68 | post.reload 69 | 70 | expect(post.raw).to eq(github_permanent_url) 71 | end 72 | 73 | it "doesn't change the post if link is broken" do 74 | post = Fabricate(:post, raw: broken_github_url) 75 | job.execute(post_id: post.id) 76 | post.reload 77 | 78 | expect(post.raw).to eq(broken_github_url) 79 | end 80 | 81 | it "works with multiple github urls in the post" do 82 | stub_request(:get, github_permanent_url).to_return(status: 200, body: "") 83 | stub_request(:get, github_permanent_url2.gsub(/#.+$/, "")).to_return(status: 200, body: "") 84 | post = Fabricate(:post, raw: "#{github_url} #{github_url2} htts://github.com") 85 | job.execute(post_id: post.id) 86 | post.reload 87 | 88 | updated_post = "#{github_permanent_url} #{github_permanent_url2} htts://github.com" 89 | expect(post.raw).to eq(updated_post) 90 | end 91 | end 92 | 93 | describe "#excluded?" do 94 | before do 95 | SiteSetting.github_permalinks_exclude = 96 | "README.md|discourse/discourse/directory/file.rb|discourse/onebox/docs/*|discourse/anotherRepo/*|someUser/*" 97 | end 98 | 99 | it "returns true when it should be excluded" do 100 | expect(job.excluded?("discourse", "discourse", "README.md")).to be true 101 | expect(job.excluded?("discourse", "discourse", "directory/file.rb")).to be true 102 | expect(job.excluded?("discourse", "onebox", "docs/file.rb")).to be true 103 | expect(job.excluded?("discourse", "anotherRepo", "directory/file.rb")).to be true 104 | expect(job.excluded?("someUser", "someRepo", "file.rb")).to be true 105 | end 106 | 107 | it "return false when url should be replaced" do 108 | expect(job.excluded?("discourse", "discourse", "directory/file2.rb")).to be false 109 | expect(job.excluded?("discourse", "onebox", "directory/file.rb")).to be false 110 | expect(job.excluded?("discourse", "discourse", "directory/included.rb")).to be false 111 | end 112 | end 113 | end 114 | -------------------------------------------------------------------------------- /spec/lib/commits_populator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rails_helper" 4 | 5 | describe DiscourseGithubPlugin::CommitsPopulator do 6 | subject(:populator) { described_class.new(repo) } 7 | 8 | let(:repo) { DiscourseGithubPlugin::GithubRepo.new(name: "discourse/discourse") } 9 | let!(:site_admin1) { Fabricate(:admin) } 10 | let!(:site_admin2) { Fabricate(:admin) } 11 | 12 | before { SiteSetting.github_badges_enabled = true } 13 | 14 | context "when invalid credentials have been provided for octokit" do 15 | before { Octokit::Client.any_instance.expects(:branches).raises(Octokit::Unauthorized) } 16 | 17 | it "disables github badges and sends a PM to the admin of the site to inform them" do 18 | populator.populate! 19 | expect(SiteSetting.github_badges_enabled).to eq(false) 20 | sent_pm = 21 | Post 22 | .joins(:topic) 23 | .includes(:topic) 24 | .where("topics.archetype = ?", Archetype.private_message) 25 | .last 26 | expect(sent_pm.topic.allowed_users.include?(site_admin1)).to eq(true) 27 | expect(sent_pm.topic.allowed_users.include?(site_admin2)).to eq(true) 28 | expect(sent_pm.topic.title).to eq( 29 | I18n.t("github_commits_populator.errors.invalid_octokit_credentials_pm_title"), 30 | ) 31 | expect(sent_pm.raw).to eq( 32 | I18n.t( 33 | "github_commits_populator.errors.invalid_octokit_credentials_pm", 34 | base_path: Discourse.base_path, 35 | ).strip, 36 | ) 37 | end 38 | end 39 | 40 | context "when the repository is not found" do 41 | before { Octokit::Client.any_instance.expects(:branches).raises(Octokit::NotFound) } 42 | 43 | it "disables github badges and sends a PM to the admin of the site to inform them" do 44 | populator.populate! 45 | expect(SiteSetting.github_badges_enabled).to eq(false) 46 | sent_pm = 47 | Post 48 | .joins(:topic) 49 | .includes(:topic) 50 | .where("topics.archetype = ?", Archetype.private_message) 51 | .last 52 | expect(sent_pm.topic.allowed_users.include?(site_admin1)).to eq(true) 53 | expect(sent_pm.topic.allowed_users.include?(site_admin2)).to eq(true) 54 | expect(sent_pm.topic.title).to eq( 55 | I18n.t("github_commits_populator.errors.repository_not_found_pm_title"), 56 | ) 57 | expect(sent_pm.raw).to eq( 58 | I18n.t( 59 | "github_commits_populator.errors.repository_not_found_pm", 60 | repo_name: repo.name, 61 | base_path: Discourse.base_path, 62 | ).strip, 63 | ) 64 | end 65 | end 66 | 67 | context "when the repository identifier is invalid" do 68 | before { Octokit::Client.any_instance.expects(:branches).raises(Octokit::InvalidRepository) } 69 | 70 | it "disables github badges and sends a PM to the admin of the site to inform them" do 71 | populator.populate! 72 | expect(SiteSetting.github_badges_enabled).to eq(false) 73 | sent_pm = 74 | Post 75 | .joins(:topic) 76 | .includes(:topic) 77 | .where("topics.archetype = ?", Archetype.private_message) 78 | .last 79 | expect(sent_pm.topic.allowed_users.include?(site_admin1)).to eq(true) 80 | expect(sent_pm.topic.allowed_users.include?(site_admin2)).to eq(true) 81 | expect(sent_pm.topic.title).to eq( 82 | I18n.t("github_commits_populator.errors.repository_identifier_invalid_pm_title"), 83 | ) 84 | expect(sent_pm.raw).to eq( 85 | I18n.t( 86 | "github_commits_populator.errors.repository_identifier_invalid_pm", 87 | repo_name: repo.name, 88 | base_path: Discourse.base_path, 89 | ).strip, 90 | ) 91 | end 92 | end 93 | 94 | context "if some other octokit error is raised" do 95 | before { Octokit::Client.any_instance.expects(:branches).raises(Octokit::Error) } 96 | 97 | it "simply logs the error and does nothing else" do 98 | populator.populate! 99 | expect(SiteSetting.github_badges_enabled).to eq(true) 100 | end 101 | end 102 | 103 | context "if GraphQL returns no data" do 104 | before do 105 | branches_body = <<~JSON 106 | [ 107 | { 108 | "name": "add-group-css-properties", 109 | "commit": { 110 | "sha": "0d67b6307042803c351599de715023841cfa9356", 111 | "url": "https://api.github.com/repos/discourse/discourse/commits/0d67b6307042803c351599de715023841cfa9356" 112 | }, 113 | "protected": false 114 | } 115 | ] 116 | JSON 117 | 118 | graphql_response = <<~JSON 119 | { 120 | "message": "Bad credentials", 121 | "documentation_url": "https://docs.github.com/graphql" 122 | } 123 | JSON 124 | 125 | stub_request( 126 | :get, 127 | "https://api.github.com/repos/discourse/discourse/branches?per_page=100", 128 | ).to_return(status: 200, body: branches_body) 129 | stub_request(:post, "https://api.github.com/graphql").to_return( 130 | status: 200, 131 | body: graphql_response, 132 | headers: { 133 | "content-type": "application/json", 134 | }, 135 | ) 136 | end 137 | 138 | it "simply logs the error and does nothing else" do 139 | expect { populator.populate! }.to raise_error(described_class::GraphQLError) 140 | end 141 | end 142 | 143 | context "if github_badges_enabled is false" do 144 | before { SiteSetting.github_badges_enabled = false } 145 | 146 | it "early returns before attempting to execute any of the commit fetching, because the plugin likely disabled itself" do 147 | Octokit::Client.any_instance.expects(:branches).never 148 | populator.populate! 149 | end 150 | end 151 | end 152 | -------------------------------------------------------------------------------- /spec/lib/github_badges_repo_setting_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rails_helper" 4 | 5 | describe GithubBadgesRepoSettingValidator do 6 | subject(:validator) { described_class.new } 7 | 8 | describe "#valid_value?" do 9 | context "when a github URL is provided" do 10 | let(:value) { "https://github.com/discourse/discourse/" } 11 | 12 | it "is ok" do 13 | expect(validator.valid_value?(value)).to eq(true) 14 | end 15 | end 16 | 17 | context "when a github repo in the format user/repo is provided" do 18 | let(:value) { "discourse/discourse-github" } 19 | 20 | it "is ok" do 21 | expect(validator.valid_value?(value)).to eq(true) 22 | end 23 | end 24 | 25 | context "when a github repo name by itself is provided" do 26 | let(:value) { "some-repo" } 27 | 28 | it "is not ok" do 29 | expect(validator.valid_value?(value)).to eq(false) 30 | end 31 | end 32 | 33 | context "when multiple valid settings are provided" do 34 | let(:value) { "discourse/discourse-github|https://github.com/discourse/discourse/" } 35 | 36 | it "is ok" do 37 | expect(validator.valid_value?(value)).to eq(true) 38 | end 39 | end 40 | 41 | context "when multiple valid settings with one invalid setting is provided" do 42 | let(:value) { "discourse/discourse-github|https://github.com/discourse/discourse/|bad-dog" } 43 | 44 | it "is not ok" do 45 | expect(validator.valid_value?(value)).to eq(false) 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/lib/github_badges_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rails_helper" 4 | 5 | describe DiscourseGithubPlugin::GithubBadges do 6 | let(:bronze_user) { Fabricate(:user) } 7 | let(:bronze_user_repo_2) { Fabricate(:user) } 8 | let(:silver_user) { Fabricate(:user) } 9 | let(:contributor) { Fabricate(:user) } 10 | let(:private_email_contributor) { Fabricate(:user) } 11 | let(:private_email_contributor2) { Fabricate(:user) } 12 | let(:merge_commit_user) { Fabricate(:user) } 13 | let(:staged_user) { Fabricate(:user, staged: true) } 14 | 15 | describe "committer and contributor badges" do 16 | before do 17 | roles = DiscourseGithubPlugin::CommitsPopulator::ROLES 18 | SiteSetting.github_badges_repos = 19 | "https://github.com/org/repo1.git|https://github.com/org/repo2.git" 20 | repo1 = DiscourseGithubPlugin::GithubRepo.repos.find { |repo| repo.name == "org/repo1" } 21 | repo2 = DiscourseGithubPlugin::GithubRepo.repos.find { |repo| repo.name == "org/repo2" } 22 | repo1.commits.create!( 23 | sha: "1", 24 | email: bronze_user.email, 25 | committed_at: 1.day.ago, 26 | role_id: roles[:committer], 27 | ) 28 | repo1.commits.create!( 29 | sha: "2", 30 | email: merge_commit_user.email, 31 | merge_commit: true, 32 | committed_at: 1.day.ago, 33 | role_id: roles[:committer], 34 | ) 35 | repo1.commits.create!( 36 | sha: "3", 37 | email: contributor.email, 38 | committed_at: 1.day.ago, 39 | role_id: roles[:contributor], 40 | ) 41 | 25.times do |n| 42 | repo1.commits.create!( 43 | sha: "blah#{n}", 44 | email: silver_user.email, 45 | committed_at: 1.day.ago, 46 | role_id: roles[:committer], 47 | ) 48 | end 49 | repo2.commits.create!( 50 | sha: "4", 51 | email: bronze_user_repo_2.email, 52 | committed_at: 2.day.ago, 53 | role_id: roles[:committer], 54 | ) 55 | 56 | UserAssociatedAccount.create!( 57 | provider_name: "github", 58 | user_id: private_email_contributor.id, 59 | info: { 60 | nickname: "bob", 61 | }, 62 | provider_uid: 100, 63 | ) 64 | repo1.commits.create!( 65 | sha: "123", 66 | email: "100+bob@users.noreply.github.com", 67 | committed_at: 1.day.ago, 68 | role_id: roles[:contributor], 69 | ) 70 | 71 | UserAssociatedAccount.create!( 72 | provider_name: "github", 73 | user_id: private_email_contributor2.id, 74 | info: { 75 | nickname: "joe", 76 | }, 77 | provider_uid: 101, 78 | ) 79 | repo1.commits.create!( 80 | sha: "124", 81 | email: "joe@users.noreply.github.com", 82 | committed_at: 1.day.ago, 83 | role_id: roles[:contributor], 84 | ) 85 | 86 | repo1.commits.create!( 87 | sha: "5", 88 | email: staged_user.email, 89 | committed_at: 1.day.ago, 90 | role_id: roles[:contributor], 91 | ) 92 | end 93 | 94 | it "granted correctly" do 95 | # initial run to seed badges and then enable them 96 | DiscourseGithubPlugin::GithubBadges.grant! 97 | 98 | contributor_bronze = DiscourseGithubPlugin::GithubBadges::BADGE_NAME_BRONZE 99 | committer_bronze = DiscourseGithubPlugin::GithubBadges::COMMITTER_BADGE_NAME_BRONZE 100 | committer_silver = DiscourseGithubPlugin::GithubBadges::COMMITTER_BADGE_NAME_SILVER 101 | 102 | users = [ 103 | bronze_user, 104 | bronze_user_repo_2, 105 | silver_user, 106 | contributor, 107 | staged_user, 108 | private_email_contributor, 109 | private_email_contributor2, 110 | merge_commit_user, 111 | ] 112 | users.each { |u| u.badges.destroy_all } 113 | 114 | [committer_bronze, committer_silver].each do |name| 115 | Badge.find_by(name: name).update!(enabled: true) 116 | end 117 | 118 | DiscourseGithubPlugin::GithubBadges.grant! 119 | users.each(&:reload) 120 | 121 | expect(merge_commit_user.badges).to eq([]) 122 | [bronze_user, bronze_user_repo_2].each_with_index do |u, ind| 123 | expect(u.badges.pluck(:name)).to eq([committer_bronze]) 124 | end 125 | expect(contributor.badges.pluck(:name)).to eq([contributor_bronze]) 126 | expect(private_email_contributor.badges.pluck(:name)).to eq([contributor_bronze]) 127 | expect(private_email_contributor2.badges.pluck(:name)).to eq([contributor_bronze]) 128 | expect(silver_user.badges.pluck(:name)).to contain_exactly(committer_bronze, committer_silver) 129 | 130 | # does not grant badges to staged users 131 | expect(staged_user.badges.first).to eq(nil) 132 | end 133 | 134 | it "does not update user title if badge is not allowed to be used as a title" do 135 | DiscourseGithubPlugin::GithubBadges.grant! 136 | silver_committer_badge = 137 | Badge.find_by(name: DiscourseGithubPlugin::GithubBadges::COMMITTER_BADGE_NAME_SILVER) 138 | 139 | silver_committer_badge.update!(enabled: true) 140 | DiscourseGithubPlugin::GithubBadges.grant! 141 | expect(silver_user.reload.title).to eq(nil) 142 | 143 | silver_committer_badge.update!(allow_title: true) 144 | DiscourseGithubPlugin::GithubBadges.grant! 145 | expect(silver_user.reload.title).to eq(silver_committer_badge.name) 146 | end 147 | 148 | it "updates existing badges" do 149 | badge = Badge.create!(name: "Great contributor", badge_type_id: 2) 150 | DiscourseGithubPlugin::GithubBadges.contributor_badges 151 | 152 | expect(badge.reload.name).to eq("Great Contributor") 153 | end 154 | end 155 | end 156 | -------------------------------------------------------------------------------- /spec/lib/github_linkback_access_token_setting_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rails_helper" 4 | 5 | describe GithubLinkbackAccessTokenSettingValidator do 6 | subject(:validator) { described_class.new } 7 | 8 | let(:value) { SecureRandom.hex(10) } 9 | 10 | describe "#valid_value?" do 11 | context "when an Octokit::Unauthorized error is raised, meaning the access token cannot access a repo" do 12 | before do 13 | setup_repos 14 | Octokit::Client.any_instance.expects(:branches).raises(Octokit::Unauthorized) 15 | end 16 | 17 | it "should fail" do 18 | expect(validator.valid_value?(value)).to eq(false) 19 | end 20 | end 21 | 22 | context "when no Octokit::Unauthorized error is raised" do 23 | it "should pass, without repos defined" do 24 | expect(validator.valid_value?(value)).to eq(true) 25 | end 26 | 27 | context "when there are repos defined" do 28 | before do 29 | setup_repos 30 | Octokit::Client.any_instance.expects(:branches).returns([]) 31 | end 32 | 33 | it "should pass if all the repos are accessible" do 34 | expect(validator.valid_value?(value)).to eq(true) 35 | end 36 | end 37 | end 38 | end 39 | 40 | def setup_repos 41 | SiteSetting.github_badges_repos = "discourse/discourse" 42 | DiscourseGithubPlugin::GithubRepo.repos 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /spec/lib/github_linkback_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rails_helper" 4 | 5 | describe GithubLinkback do 6 | let(:github_commit_link) do 7 | "https://github.com/discourse/discourse/commit/76981605fa10975e2e7af457e2f6a31909e0c811" 8 | end 9 | let(:github_commit_link_with_anchor) { "#{github_commit_link}#anchor" } 10 | let(:github_issue_link) { "https://github.com/discourse/discourse/issues/123" } 11 | let(:github_pr_link) { "https://github.com/discourse/discourse/pull/701" } 12 | let(:github_pr_files_link) { "https://github.com/discourse/discourse/pull/701/files" } 13 | let(:github_pr_link_wildcard) { "https://github.com/discourse/discourse-github-linkback/pull/3" } 14 | 15 | let(:post) { Fabricate(:post, raw: <<~RAW) } 16 | cool post 17 | 18 | #{github_commit_link} 19 | 20 | https://eviltrout.com/not-a-gh-link 21 | 22 | #{github_commit_link} 23 | 24 | #{github_commit_link_with_anchor} 25 | 26 | https://github.com/eviltrout/tis-100/commit/e22b23f354e3a1c31bc7ad37a6a309fd6daf18f4 27 | 28 | #{github_issue_link} 29 | 30 | #{github_pr_link} 31 | 32 | #{github_pr_files_link} 33 | 34 | i have no idea what i'm linking back to 35 | 36 | #{github_pr_link_wildcard} 37 | 38 | end_of_transmission 39 | 40 | RAW 41 | 42 | describe "#should_enqueue?" do 43 | let(:post_without_link) { Fabricate.build(:post, raw: "Hello github!") } 44 | let(:small_action_post) do 45 | Fabricate.build( 46 | :post, 47 | post_type: Post.types[:small_action], 48 | raw: 49 | "https://github.com/discourse/discourse/commit/5be9bee2307dd517c26e6ef269471aceba5d5acf", 50 | ) 51 | end 52 | let(:post_with_link) do 53 | Fabricate.build( 54 | :post, 55 | raw: 56 | "https://github.com/discourse/discourse/commit/5be9bee2307dd517c26e6ef269471aceba5d5acf", 57 | ) 58 | end 59 | 60 | it "returns false when the feature is disabled" do 61 | SiteSetting.github_linkback_enabled = false 62 | expect(GithubLinkback.new(post_with_link).should_enqueue?).to eq(false) 63 | end 64 | 65 | it "returns false without a post" do 66 | SiteSetting.github_linkback_enabled = true 67 | expect(GithubLinkback.new(nil).should_enqueue?).to eq(false) 68 | end 69 | 70 | it "returns false if the post is not a regular post" do 71 | SiteSetting.github_linkback_enabled = true 72 | expect(GithubLinkback.new(small_action_post).should_enqueue?).to eq(false) 73 | end 74 | 75 | it "returns false when the post doesn't have the `github.com` in it" do 76 | SiteSetting.github_linkback_enabled = true 77 | expect(GithubLinkback.new(post_without_link).should_enqueue?).to eq(false) 78 | end 79 | 80 | it "returns true when the feature is enabled" do 81 | SiteSetting.github_linkback_enabled = true 82 | expect(GithubLinkback.new(post_with_link).should_enqueue?).to eq(true) 83 | end 84 | 85 | describe "private_message" do 86 | it "doesn't enqueue private messages" do 87 | SiteSetting.github_linkback_enabled = true 88 | private_topic = Fabricate(:private_message_topic) 89 | private_post = 90 | Fabricate( 91 | :post, 92 | topic: private_topic, 93 | raw: "this post http://github.com should not enqueue", 94 | ) 95 | expect(GithubLinkback.new(private_post).should_enqueue?).to eq(false) 96 | end 97 | end 98 | 99 | describe "unlisted topics" do 100 | it "doesn't enqueue unlisted topics" do 101 | SiteSetting.github_linkback_enabled = true 102 | unlisted_topic = Fabricate(:topic, visible: false) 103 | unlisted_post = 104 | Fabricate( 105 | :post, 106 | topic: unlisted_topic, 107 | raw: "this post http://github.com should not enqueue", 108 | ) 109 | expect(GithubLinkback.new(unlisted_post).should_enqueue?).to eq(false) 110 | end 111 | end 112 | end 113 | 114 | describe "#github_links" do 115 | it "returns an empty array with no projects" do 116 | SiteSetting.github_linkback_projects = "" 117 | links = GithubLinkback.new(post).github_links 118 | expect(links).to eq([]) 119 | end 120 | 121 | it "doesn't return links that have already been posted" do 122 | SiteSetting.github_linkback_projects = 123 | "discourse/discourse|eviltrout/ember-performance|discourse/*" 124 | 125 | post.custom_fields[GithubLinkback.field_for(github_commit_link)] = "true" 126 | post.custom_fields[GithubLinkback.field_for(github_issue_link)] = "true" 127 | post.custom_fields[GithubLinkback.field_for(github_pr_link)] = "true" 128 | post.custom_fields[GithubLinkback.field_for(github_pr_link_wildcard)] = "true" 129 | post.save_custom_fields 130 | 131 | links = GithubLinkback.new(post).github_links 132 | expect(links.size).to eq(0) 133 | end 134 | 135 | it "should return the urls for the selected projects" do 136 | SiteSetting.github_linkback_projects = 137 | "discourse/discourse|eviltrout/ember-performance|discourse/*" 138 | links = GithubLinkback.new(post).github_links 139 | expect(links.size).to eq(4) 140 | 141 | expect(links[0].url).to eq(github_commit_link) 142 | expect(links[0].project).to eq("discourse/discourse") 143 | expect(links[0].sha).to eq("76981605fa10975e2e7af457e2f6a31909e0c811") 144 | expect(links[0].type).to eq(:commit) 145 | 146 | expect(links[1].url).to eq(github_issue_link) 147 | expect(links[1].project).to eq("discourse/discourse") 148 | expect(links[1].issue_number).to eq(123) 149 | expect(links[1].type).to eq(:issue) 150 | 151 | expect(links[2].url).to eq(github_pr_link) 152 | expect(links[2].project).to eq("discourse/discourse") 153 | expect(links[2].pr_number).to eq(701) 154 | expect(links[2].type).to eq(:pr) 155 | 156 | expect(links[3].url).to eq(github_pr_link_wildcard) 157 | expect(links[3].project).to eq("discourse/discourse-github-linkback") 158 | expect(links[3].pr_number).to eq(3) 159 | expect(links[3].type).to eq(:pr) 160 | end 161 | end 162 | 163 | describe "#create" do 164 | before { SiteSetting.github_linkback_projects = "discourse/discourse|discourse/*" } 165 | 166 | it "returns an empty array without an access token" do 167 | expect(GithubLinkback.new(post).create).to be_blank 168 | end 169 | 170 | context "with an access token" do 171 | let(:headers) do 172 | { 173 | "Authorization" => "token abcdef", 174 | "Content-Type" => "application/json", 175 | "Host" => "api.github.com", 176 | "User-Agent" => "Discourse-Github-Linkback", 177 | } 178 | end 179 | 180 | before do 181 | SiteSetting.github_linkback_access_token = "abcdef" 182 | 183 | stub_request( 184 | :post, 185 | "https://api.github.com/repos/discourse/discourse/commits/76981605fa10975e2e7af457e2f6a31909e0c811/comments", 186 | ).with(headers: headers).to_return(status: 200, body: "", headers: {}) 187 | 188 | stub_request( 189 | :post, 190 | "https://api.github.com/repos/discourse/discourse/issues/123/comments", 191 | ).with(headers: headers).to_return(status: 200, body: "", headers: {}) 192 | 193 | stub_request( 194 | :post, 195 | "https://api.github.com/repos/discourse/discourse/issues/701/comments", 196 | ).with(headers: headers).to_return(status: 200, body: "", headers: {}) 197 | 198 | stub_request( 199 | :post, 200 | "https://api.github.com/repos/discourse/discourse-github-linkback/issues/3/comments", 201 | ).with(headers: headers).to_return(status: 200, body: "", headers: {}) 202 | end 203 | 204 | it "returns the URLs it linked to and creates custom fields" do 205 | links = GithubLinkback.new(post).create 206 | expect(links.size).to eq(4) 207 | 208 | expect(links[0].url).to eq(github_commit_link) 209 | field = GithubLinkback.field_for(github_commit_link) 210 | expect(post.custom_fields[field]).to be_present 211 | 212 | expect(links[1].url).to eq(github_issue_link) 213 | field = GithubLinkback.field_for(github_issue_link) 214 | expect(post.custom_fields[field]).to be_present 215 | 216 | expect(links[2].url).to eq(github_pr_link) 217 | field = GithubLinkback.field_for(github_pr_link) 218 | expect(post.custom_fields[field]).to be_present 219 | 220 | expect(links[3].url).to eq(github_pr_link_wildcard) 221 | field = GithubLinkback.field_for(github_pr_link_wildcard) 222 | expect(post.custom_fields[field]).to be_present 223 | end 224 | 225 | it "should create linkback for <= SiteSetting.github_linkback_maximum_links urls" do 226 | SiteSetting.github_linkback_maximum_links = 2 227 | post = Fabricate(:post, raw: "#{github_pr_link} #{github_issue_link}") 228 | links = GithubLinkback.new(post).create 229 | expect(links.size).to eq(2) 230 | end 231 | 232 | it "should skip linkback for > SiteSetting.github_linkback_maximum_links urls" do 233 | SiteSetting.github_linkback_maximum_links = 1 234 | post = Fabricate(:post, raw: "#{github_pr_link} #{github_issue_link}") 235 | links = GithubLinkback.new(post).create 236 | expect(links.size).to eq(0) 237 | end 238 | 239 | it "should create linkback for <= SiteSetting.github_linkback_maximum_links unique urls" do 240 | SiteSetting.github_linkback_maximum_links = 1 241 | post = Fabricate(:post, raw: "#{github_pr_link} #{github_pr_link} #{github_pr_link}") 242 | links = GithubLinkback.new(post).create 243 | expect(links.size).to eq(1) 244 | end 245 | end 246 | end 247 | end 248 | -------------------------------------------------------------------------------- /spec/lib/github_permalinks_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rails_helper" 4 | 5 | describe GithubPermalinks do 6 | context "when it doesn't contain github link to the file" do 7 | let(:post) { Fabricate(:post, raw: "there is no github link") } 8 | 9 | it "it does not run the job" do 10 | Jobs.expects(:cancel_scheduled_job).never 11 | GithubPermalinks.replace_github_non_permalinks(post) 12 | end 13 | end 14 | 15 | context "when it contains github link" do 16 | let(:post) do 17 | Fabricate( 18 | :post, 19 | raw: "https://github.com/discourse/onebox/blob/master/lib/onebox/engine/gfycat_onebox.rb", 20 | ) 21 | end 22 | 23 | it "ensures only one job is scheduled right after the editing_grace_period" do 24 | freeze_time 25 | 26 | Jobs 27 | .expects(:cancel_scheduled_job) 28 | .with(:replace_github_non_permalinks, post_id: post.id) 29 | .once 30 | delay = SiteSetting.editing_grace_period + 1 31 | 32 | expect_enqueued_with( 33 | job: :replace_github_non_permalinks, 34 | args: { 35 | post_id: post.id, 36 | bypass_bump: false, 37 | }, 38 | at: Time.zone.now + delay, 39 | ) { GithubPermalinks.replace_github_non_permalinks(post) } 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/models/github_repo_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rails_helper" 4 | 5 | describe DiscourseGithubPlugin::GithubRepo do 6 | it "strips .git from url" do 7 | SiteSetting.github_badges_repos = "https://github.com/discourse/discourse.git" 8 | repo = DiscourseGithubPlugin::GithubRepo.repos.first 9 | expect(repo.name).to eq("discourse/discourse") 10 | end 11 | 12 | it "strips trailing slash from url" do 13 | SiteSetting.github_badges_repos = "https://github.com/discourse/discourse/" 14 | repo = DiscourseGithubPlugin::GithubRepo.repos.first 15 | expect(repo.name).to eq("discourse/discourse") 16 | end 17 | 18 | it "doesn't raise an error when the site setting follows the user/repo format" do 19 | SiteSetting.github_badges_repos = "discourse/discourse-github" 20 | repo = DiscourseGithubPlugin::GithubRepo.repos.first 21 | expect(repo.name).to eq("discourse/discourse-github") 22 | 23 | SiteSetting.github_badges_repos = "discourse/somerepo_with-numbers7" 24 | repo = DiscourseGithubPlugin::GithubRepo.repos.first 25 | expect(repo.name).to eq("discourse/somerepo_with-numbers7") 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/system/core_features_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe "Core features", type: :system do 4 | before { enable_current_plugin } 5 | 6 | it_behaves_like "having working core features" 7 | end 8 | -------------------------------------------------------------------------------- /stylelint.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | extends: ["@discourse/lint-configs/stylelint"], 3 | }; 4 | -------------------------------------------------------------------------------- /translator.yml: -------------------------------------------------------------------------------- 1 | # Configuration file for discourse-translator-bot 2 | 3 | files: 4 | - source_path: config/locales/client.en.yml 5 | destination_path: client.yml 6 | - source_path: config/locales/server.en.yml 7 | destination_path: server.yml 8 | --------------------------------------------------------------------------------