reorganisation du code

master
Jérémy Lecour 2019-01-27 22:13:00 +01:00
parent 6049c4d5c3
commit bf3b280064
7 changed files with 564 additions and 356 deletions

View File

@ -6,3 +6,4 @@ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
gem "rest-client"
gem "pry"
gem "naught"

View File

@ -10,6 +10,7 @@ GEM
mime-types (3.2.2)
mime-types-data (~> 3.2015)
mime-types-data (3.2018.0812)
naught (1.1.0)
netrc (0.11.0)
pry (0.11.3)
coderay (~> 1.1.0)
@ -26,8 +27,9 @@ PLATFORMS
ruby
DEPENDENCIES
naught
pry
rest-client
BUNDLED WITH
1.16.2
1.17.2

196
gitea_api.rb Normal file
View File

@ -0,0 +1,196 @@
require 'rest-client'
require 'json'
require 'naught'
require 'pry'
module Gitea
class API
attr_accessor :api_key
attr_accessor :base_url
attr_accessor :http_headers
attr_accessor :rest_client
def initialize(api_key:, base_url: "https://gitea.evolix.org", rest_client: RestClient)
@api_key = api_key
@base_url = base_url
@http_headers = {
"Authorization" => "token #{api_key}",
"Content-Type" => "application/json",
"Accept" => "application/json",
}
@rest_client = rest_client
end
def create_issue(issue:, project_name:, user_name:)
puts "Gitea::API.create_issue(project_name: #{project_name})"
response = rest_client.post(
"#{base_url}/api/v1/repos/#{project_name}/issues",
JSON.generate(issue),
http_headers.merge("Sudo" => user_name)
) { |response, request, result, &block|
case response.code
when 201
if response.body.empty?
{}
else
JSON.parse(response.body)
end
else
binding.pry
end
}
rescue => ex
binding.pry
end
def create_comment(comment:, project_name:, issue_number:, user_name:)
puts "Gitea::API.create_comment(project_name: #{project_name}, issue_number: #{issue_number})"
response = rest_client.post(
"#{base_url}/api/v1/repos/#{project_name}/issues/#{issue_number}/comments",
JSON.generate(comment),
http_headers.merge("Sudo" => user_name)
) { |response, request, result, &block|
case response.code
when 201
if response.body.empty?
{}
else
JSON.parse(response.body)
end
else
binding.pry
end
}
rescue => ex
binding.pry
end
def get_repo(org_name:, repo_name:)
puts "Gitea::API.get_repository(org_name: #{org_name}, repo_name: #{repo_name})"
rest_client.get(
"#{base_url}/api/v1/repos/#{org_name}/#{repo_name}",
http_headers
) { |response, request, result, &block|
case response.code
when 200
raw_repository = JSON.parse(response.body)
Repository.new(raw_repository)
else
NullRepository.new
end
}
rescue => ex
binding.pry
end
def create_user_repository(repository:)
puts "Gitea::API.create_user_repository()"
response = rest_client.post(
"#{base_url}/api/v1/user/repos",
JSON.generate(repository),
http_headers
) { |response, request, result, &block|
case response.code
when 201
if response.body.empty?
{}
else
JSON.parse(response.body)
end
else
binding.pry
end
}
rescue => ex
binding.pry
end
def create_org_repository(org_name:, repository:)
puts "Gitea::API.create_org_repository(org_name: #{org_name})"
response = rest_client.post(
"#{base_url}/api/v1/orgs/#{org_name}/repos",
JSON.generate(repository),
http_headers
) { |response, request, result, &block|
case response.code
when 201
if response.body.empty?
{}
else
JSON.parse(response.body)
end
else
binding.pry
end
}
rescue => ex
binding.pry
end
def delete_repository(org_name:, repo_name:)
puts "Gitea::API.delete_repository(org_name: #{org_name}, repo_name: #{repo_name})"
response = rest_client.delete(
"#{base_url}/api/v1/repos/#{org_name}/#{repo_name}",
http_headers
) { |response, request, result, &block|
case response.code
when 201
if response.body.empty?
{}
else
JSON.parse(response.body)
end
else
binding.pry
end
}
rescue => ex
binding.pry
end
def add_collaborator(org_name:, repo_name:, collaborator_name:, permissions: {})
puts "Gitea::API.add_collaborator(org_name: #{org_name}, repo_name: #{repo_name}, collaborator_name: #{collaborator_name})"
rest_client.put(
"#{base_url}/api/v1/repos/#{org_name}/#{repo_name}/collaborators/#{collaborator_name}",
JSON.generate(permissions),
http_headers
) { |response, request, result, &block|
case response.code
when 204
nil
else
binding.pry
end
}
rescue => ex
binding.pry
end
end
class Repository
attr_reader :id
def initialize(repository)
@repository = repository
end
def id
@repository.fetch("id")
end
end
NullRepository = Naught.build
end

76
main.rb
View File

@ -1,26 +1,28 @@
require_relative "redmine-to-gitea"
require_relative 'textile-to-markdown'
fake_user_names_proc = Proc.new { |x| "redmine_importer" }
user_names_proc = Proc.new { |x|
# case x
# when 488 then "abenmiloud"
# when 40 then "benpro"
# when 443 then "btatu"
# when 406 then "emorino"
# when 3 then "gcolpart"
# when 408 then "jcougnoux"
# when 400 then "jdubois"
# when 391 then "jlecour"
# when 430 then "lpoujol"
# when 398 then "pdiogoantunes"
# when 7 then "romain"
# when 240 then "drustan"
# when 344 then "vlaborie"
# when 275 then "jmartinez"
# when 284 then "agobin"
# when 527 then "sbencherif"
# else
case x
when 488 then "abenmiloud"
when 40 then "benpro"
when 443 then "btatu"
when 406 then "emorino"
when 3 then "gcolpart"
when 408 then "jcougnoux"
when 400 then "jdubois"
when 391 then "jlecour"
when 430 then "lpoujol"
when 398 then "pdiogoantunes"
when 7 then "romain"
when 240 then "drustan"
when 344 then "vlaborie"
when 275 then "jmartinez"
when 284 then "agobin"
when 527 then "sbencherif"
else
"redmine_importer"
# end
end
}
statuses_proc = Proc.new { |x|
@ -60,20 +62,40 @@ priorities_proc = Proc.new { |x|
}
mapping = RedmineToGitea::Mapping.new(
user_names_proc: user_names_proc,
user_names_proc: fake_user_names_proc,
statuses_proc: statuses_proc,
closed_statuses_proc: closed_statuses_proc,
priorities_proc: priorities_proc
)
migrator = RedmineToGitea::Migrator.new(
redmine_api: Redmine::API.new(api_key: ENV.fetch('REDMINE_API_KEY')),
gitea_api: Gitea::API.new(api_key: ENV.fetch('GITEA_API_KEY')),
mapping: mapping,
issue_formatter: IssueFormatter.new(mapping: mapping, date_format: "%Y-%m-%d %H:%M:%S")
settings = RedmineToGitea::Settings.new(
mapping: mapping,
date_format: "%Y-%m-%d %H:%M:%S",
textile_to_markdown: TextileToMarkdown,
)
migrator = RedmineToGitea::Migrator.new(
redmine_api: Redmine::API.new(api_key: ENV.fetch('REDMINE_API_KEY')),
gitea_api: Gitea::API.new(api_key: ENV.fetch('GITEA_API_KEY')),
settings: settings
)
repository = {
"name": "tests-redmine-to-gitea",
"private": true,
}
migrator.reset_project(
org_name: "jlecour",
repository: repository,
collaborators: [
"redmine_importer"
]
)
# evolix-private: 231
# ansible-roles: 226
# evogestion: 226
migrator.issues(
redmine_project: 226,
redmine_project: 209,
gitea_project: "jlecour/tests-redmine-to-gitea"
)

View File

@ -1,224 +1,5 @@
require 'rest-client'
require 'json'
require 'pry'
require_relative 'textile-to-markdown'
module Gitea
class API
attr_accessor :api_key
attr_accessor :base_url
attr_accessor :http_headers
attr_accessor :rest_client
def initialize(api_key:, base_url: "https://gitea.evolix.org", rest_client: RestClient)
@api_key = api_key
@base_url = base_url
@http_headers = {
"Authorization" => "token #{api_key}",
"Content-Type" => "application/json",
"Accept" => "application/json",
}
@rest_client = rest_client
end
def post_issue(issue:, project_name:, user_name:)
puts "Gitea::API.post_issue(project_name: #{project_name})"
response = rest_client.post(
"#{base_url}/api/v1/repos/#{project_name}/issues",
JSON.generate(issue),
http_headers.merge("Sudo" => user_name)
)
result = JSON.parse(response.body)
rescue => ex
binding.pry
end
def post_comment(comment:, project_name:, issue_number:, user_name:)
puts "Gitea::API.post_comment(project_name: #{project_name}, issue_number: #{issue_number})"
response = rest_client.post(
"#{base_url}/api/v1/repos/#{project_name}/issues/#{issue_number}/comments",
JSON.generate(comment),
http_headers.merge("Sudo" => user_name)
)
result = JSON.parse(response.body)
rescue => ex
binding.pry
end
end
end
module Redmine
class API
attr_accessor :api_key
attr_accessor :base_url
attr_accessor :http_headers
attr_accessor :rest_client
def initialize(api_key:, base_url: "https://forge.evolix.org", rest_client: RestClient)
@api_key = api_key
@base_url = base_url
@http_headers = {
"X-Redmine-API-Key" => api_key,
"Content-Type" => "application/json",
"Accept" => "application/json"
}
@rest_client = rest_client
end
def get_issues(project_id:, offset: 0, limit: 25)
puts "Redmine::API.get_issues(project_id: #{project_id}, offset: #{offset}, limit: #{limit})"
response = rest_client.get(
"#{base_url}/issues.json?project_id=#{project_id}&offset=#{offset}&limit=#{limit}",
http_headers
)
result = JSON.parse(response.body)
ResultSet.new(
results: result.fetch("issues").map { |issue| Issue.new(issue) },
total: result.fetch("total_count"),
offset: result.fetch("offset"),
limit: result.fetch("limit")
)
rescue => ex
binding.pry
end
def get_issue(id:)
puts "Redmine::API.get_issue(id: #{id})"
response = rest_client.get(
"#{base_url}/issues/#{id}.json?include=journals",
http_headers
)
result = JSON.parse(response.body)
Issue.new(result.fetch("issue"))
rescue => ex
binding.pry
end
end
class ResultSet
attr_reader :results
attr_reader :total
attr_reader :offset
attr_reader :limit
def initialize(results:, total:, offset:, limit:)
@results = results
@total = total
@offset = offset
@limit = limit
end
def remaining_results?
(offset + limit) < total
end
end
class Issue
attr_reader :issue
def initialize(issue)
@issue = issue
end
def id
issue.fetch("id")
end
def project_name
issue.fetch("project", {}).fetch("name")
end
def tracker_name
issue.fetch("tracker", {}).fetch("name")
end
def status_id
issue.fetch("status", {}).fetch("id")
end
def status_name
issue.fetch("status", {}).fetch("name")
end
def category_name
if issue.key?("category")
issue.fetch("category", {}).fetch("name")
end
end
def author_id
issue.fetch("author", {}).fetch("id")
end
def author_name
issue.fetch("author", {}).fetch("name")
end
def assigned_to
if issue.key?("assigned_to")
issue.fetch("assigned_to").fetch("id")
end
end
def start_date
issue.fetch("start_date")
end
def done_ratio
issue.fetch("done_ratio")
end
def created_on
issue.fetch("created_on")
end
def updated_on
issue.fetch("updated_on")
end
def subject
issue.fetch("subject")
end
def description
issue.fetch("description")
end
def journals
if issue.key?("journals")
issue.fetch("journals", {}).map { |journal|
Journal.new(journal)
}
else
[]
end
end
end
class Journal
attr_reader :journal
def initialize(journal)
@journal = journal
end
def user_id
journal.fetch("user", {}).fetch("id")
end
def user_name
journal.fetch("user", {}).fetch("name")
end
def created_on
journal.fetch("created_on")
end
def details
journal.fetch("details", [])
end
def notes
journal.fetch("notes", "")
end
def private_notes?
journal.fetch("private_notes", false)
end
end
end
require_relative 'redmine_api'
require_relative 'gitea_api'
module RedmineToGitea
@ -238,8 +19,7 @@ module RedmineToGitea
def user_name(redmine_user_id)
return nil if redmine_user_id.nil?
# user_names_proc.call(redmine_user_id)
"redmine_importer"
user_names_proc.call(redmine_user_id)
end
def status(redmine_status_id)
@ -255,17 +35,51 @@ module RedmineToGitea
end
end
class Settings
attr_accessor :mapping
attr_accessor :date_format
attr_accessor :textile_to_markdown
def initialize(mapping:, date_format:, textile_to_markdown: Proc.new { |x| x } )
@mapping = mapping
@date_format = date_format
@textile_to_markdown = textile_to_markdown
end
end
class Migrator
attr_accessor :redmine_api
attr_accessor :gitea_api
attr_accessor :settings
attr_accessor :issue_formatter
attr_accessor :mapping
def initialize(redmine_api:, gitea_api:, mapping: Mapping.new, issue_formatter: IssueFormatter.new)
@redmine_api = redmine_api
@gitea_api = gitea_api
@mapping = mapping
@issue_formatter = issue_formatter
def initialize(redmine_api:, gitea_api:, settings: Settings.new)
@redmine_api = redmine_api
@gitea_api = gitea_api
@settings = settings
@issue_formatter = IssueFormatter.new(
mapping: mapping,
date_format: settings.date_format,
textile_to_markdown: settings.textile_to_markdown
)
end
def reset_project(org_name:, repository:, collaborators: [])
repo_name = repository.fetch(:name)
old_repository = gitea_api.get_repo(org_name: org_name, repo_name: repo_name)
if old_repository.id
gitea_api.delete_repository(org_name: org_name, repo_name: repo_name)
end
# gitea_api.create_org_repository(org_name: org_name, repository: repository)
gitea_api.create_user_repository(repository: repository)
collaborators.each do |collaborator|
gitea_api.add_collaborator(org_name: org_name, repo_name: repo_name, collaborator_name: collaborator)
end
rescue => ex
binding.pry
end
def issues(redmine_project:, gitea_project:)
@ -294,6 +108,10 @@ module RedmineToGitea
private
def mapping
settings.mapping
end
def import_issue(redmine_issue, gitea_project)
gitea_issue = {
"title" => redmine_issue.subject,
@ -309,7 +127,7 @@ module RedmineToGitea
user = mapping.user_name(redmine_issue.author_id)
created_gitea_issue = gitea_api.post_issue(
created_gitea_issue = gitea_api.create_issue(
issue: gitea_issue,
project_name: gitea_project,
user_name: user
@ -322,7 +140,7 @@ module RedmineToGitea
}
user_name = mapping.user_name(journal.user_id)
gitea_api.post_comment(
gitea_api.create_comment(
comment: gitea_comment,
project_name: gitea_project,
issue_number: gitea_issue_number,
@ -334,111 +152,111 @@ module RedmineToGitea
binding.pry
end
end
class IssueFormatter
attr_accessor :mapping
attr_accessor :date_format
attr_accessor :textile_to_markdown
class IssueFormatter
attr_accessor :mapping
attr_accessor :date_format
attr_accessor :textile_to_markdown
def initialize(mapping:, date_format: "%c", textile_to_markdown: TextileToMarkdown)
@mapping = mapping
@date_format = date_format
@textile_to_markdown = textile_to_markdown
end
def description(issue)
issue_body = "> **Issue imported from forge.evolix.org on #{Time.now.strftime(date_format)}**"
issue_body += "\n> * Project: #{issue.project_name}"
issue_body += "\n> * Issue: #{issue.id}"
issue_body += "\n> * Tracker: #{issue.tracker_name}"
issue_body += "\n> * Status: #{issue.status_name}"
issue_body += "\n> * Author: #{issue.author_name}"
issue_body += "\n> * Category: #{issue.category_name}"
issue_body += "\n> * Started at: #{DateTime.parse(issue.start_date).strftime(date_format)}"
issue_body += "\n> * Done ratio: #{issue.done_ratio}"
issue_body += "\n> * Created at: #{DateTime.parse(issue.created_on).strftime(date_format)}"
issue_body += "\n> * Updated at: #{DateTime.parse(issue.updated_on).strftime(date_format)}"
issue_body += "\n\n"
issue_body += textile_to_markdown.convert(issue.description)
issue_body
rescue => ex
binding.pry
end
def comment(journal)
comment_body = "> **Comment imported from forge.evolix.org on #{Time.now.strftime(date_format)}**"
comment_body += "\n> * Author: #{journal.user_name}" if journal.user_name
comment_body += "\n> * Created at: #{DateTime.parse(journal.created_on).strftime(date_format)}" if journal.created_on
comment_details(journal.details).each do |detail|
comment_body += "\n> * #{detail}"
def initialize(mapping:, date_format: "%c", textile_to_markdown: )
@mapping = mapping
@date_format = date_format
@textile_to_markdown = textile_to_markdown
end
unless journal.notes.empty?
comment_body += "\n\n"
if journal.private_notes?
comment_body += "_This note is private its content has not been imported_"
else
comment_body += textile_to_markdown.convert(journal.notes)
def description(issue)
issue_body = "> **Issue imported from forge.evolix.org on #{Time.now.strftime(date_format)}**"
issue_body += "\n> * Project: #{issue.project_name}"
issue_body += "\n> * Issue: #{issue.id}"
issue_body += "\n> * Tracker: #{issue.tracker_name}"
issue_body += "\n> * Status: #{issue.status_name}"
issue_body += "\n> * Author: #{issue.author_name}"
issue_body += "\n> * Category: #{issue.category_name}"
issue_body += "\n> * Started at: #{DateTime.parse(issue.start_date).strftime(date_format)}"
issue_body += "\n> * Done ratio: #{issue.done_ratio}"
issue_body += "\n> * Created at: #{DateTime.parse(issue.created_on).strftime(date_format)}"
issue_body += "\n> * Updated at: #{DateTime.parse(issue.updated_on).strftime(date_format)}"
issue_body += "\n\n"
issue_body += textile_to_markdown.call(issue.description)
issue_body
rescue => ex
binding.pry
end
def comment(journal)
comment_body = "> **Comment imported from forge.evolix.org on #{Time.now.strftime(date_format)}**"
comment_body += "\n> * Author: #{journal.user_name}" if journal.user_name
comment_body += "\n> * Created at: #{DateTime.parse(journal.created_on).strftime(date_format)}" if journal.created_on
comment_details(journal.details).each do |detail|
comment_body += "\n> * #{detail}"
end
end
comment_body
rescue => ex
binding.pry
end
private
def comment_details(details)
results = []
details.each do |detail|
if detail['property'] == "attr"
case detail['name']
when "status_id" then
old = mapping.status(detail['old_value'])
new = mapping.status(detail['new_value'])
results.push "Status: #{old}#{new}"
when "priority_id" then
old = mapping.priority(detail['old_value'])
new = mapping.priority(detail['new_value'])
results.push "Priority: #{old}#{new}"
when "done_ratio" then
old = detail['old_value']
new = detail['new_value']
results.push "Done ratio: #{old}% → #{new}%"
when "project_id" then
old = detail['old_value'] || "ø"
new = detail['new_value'] || "ø"
results.push "Project: #{old}#{new}"
# when "tracker_id" then
# old = detail['old_value'] || "ø"
# new = detail['new_value'] || "ø"
# results.push "Tracker: #{old} → #{new}"
when "assigned_to_id" then
old = mapping.user_name(detail['old_value']) || "ø"
new = mapping.user_name(detail['new_value']) || "ø"
results.push "Assigned to: #{old}#{new}"
when "fixed_version_id" then
old = detail['old_value'] || "ø"
new = detail['new_value'] || "ø"
results.push "Version: #{old}#{new}"
when "subject", "description"
next
unless journal.notes.empty?
comment_body += "\n\n"
if journal.private_notes?
comment_body += "_This note is private its content has not been imported_"
else
old = detail['old_value'] || "ø"
new = detail['new_value'] || "ø"
results.push "#{detail['name']}: #{old}#{new}"
comment_body += textile_to_markdown.call(journal.notes)
end
elsif detail['property'] == "relation"
results.push "Related to: #{detail['new_value']}"
else
next
end
comment_body
rescue => ex
binding.pry
end
results
private
def comment_details(details)
results = []
details.each do |detail|
if detail['property'] == "attr"
case detail['name']
when "status_id" then
old = mapping.status(detail['old_value'])
new = mapping.status(detail['new_value'])
results.push "Status: #{old}#{new}"
when "priority_id" then
old = mapping.priority(detail['old_value'])
new = mapping.priority(detail['new_value'])
results.push "Priority: #{old}#{new}"
when "done_ratio" then
old = detail['old_value']
new = detail['new_value']
results.push "Done ratio: #{old}% → #{new}%"
when "project_id" then
old = detail['old_value'] || "ø"
new = detail['new_value'] || "ø"
results.push "Project: #{old}#{new}"
# when "tracker_id" then
# old = detail['old_value'] || "ø"
# new = detail['new_value'] || "ø"
# results.push "Tracker: #{old} → #{new}"
when "assigned_to_id" then
old = mapping.user_name(detail['old_value']) || "ø"
new = mapping.user_name(detail['new_value']) || "ø"
results.push "Assigned to: #{old}#{new}"
when "fixed_version_id" then
old = detail['old_value'] || "ø"
new = detail['new_value'] || "ø"
results.push "Version: #{old}#{new}"
when "subject", "description"
next
else
old = detail['old_value'] || "ø"
new = detail['new_value'] || "ø"
results.push "#{detail['name']}: #{old}#{new}"
end
elsif detail['property'] == "relation"
results.push "Related to: #{detail['new_value']}"
else
next
end
end
results
end
end
end

169
redmine_api.rb Normal file
View File

@ -0,0 +1,169 @@
require 'rest-client'
require 'json'
require 'pry'
module Redmine
class API
attr_accessor :api_key
attr_accessor :base_url
attr_accessor :http_headers
attr_accessor :rest_client
def initialize(api_key:, base_url: "https://forge.evolix.org", rest_client: RestClient)
@api_key = api_key
@base_url = base_url
@http_headers = {
"X-Redmine-API-Key" => api_key,
"Content-Type" => "application/json",
"Accept" => "application/json"
}
@rest_client = rest_client
end
def get_issues(project_id:, offset: 0, limit: 25)
puts "Redmine::API.get_issues(project_id: #{project_id}, offset: #{offset}, limit: #{limit})"
response = rest_client.get(
"#{base_url}/issues.json?project_id=#{project_id}&offset=#{offset}&limit=#{limit}",
http_headers
)
result = JSON.parse(response.body)
ResultSet.new(
results: result.fetch("issues").map { |issue| Issue.new(issue) },
total: result.fetch("total_count"),
offset: result.fetch("offset"),
limit: result.fetch("limit")
)
rescue => ex
binding.pry
end
def get_issue(id:)
puts "Redmine::API.get_issue(id: #{id})"
response = rest_client.get(
"#{base_url}/issues/#{id}.json?include=journals",
http_headers
)
result = JSON.parse(response.body)
Issue.new(result.fetch("issue"))
rescue => ex
binding.pry
end
end
class ResultSet
attr_reader :results
attr_reader :total
attr_reader :offset
attr_reader :limit
def initialize(results:, total:, offset:, limit:)
@results = results
@total = total
@offset = offset
@limit = limit
end
def remaining_results?
(offset + limit) < total
end
end
class Issue
attr_reader :issue
def initialize(issue)
@issue = issue
end
def id
issue.fetch("id")
end
def project_name
issue.fetch("project", {}).fetch("name")
end
def tracker_name
issue.fetch("tracker", {}).fetch("name")
end
def status_id
issue.fetch("status", {}).fetch("id")
end
def status_name
issue.fetch("status", {}).fetch("name")
end
def category_name
if issue.key?("category")
issue.fetch("category", {}).fetch("name")
end
end
def author_id
issue.fetch("author", {}).fetch("id")
end
def author_name
issue.fetch("author", {}).fetch("name")
end
def assigned_to
if issue.key?("assigned_to")
issue.fetch("assigned_to").fetch("id")
end
end
def start_date
issue.fetch("start_date")
end
def done_ratio
issue.fetch("done_ratio")
end
def created_on
issue.fetch("created_on")
end
def updated_on
issue.fetch("updated_on")
end
def subject
issue.fetch("subject")
end
def description
issue.fetch("description")
end
def journals
if issue.key?("journals")
issue.fetch("journals", {}).map { |journal|
Journal.new(journal)
}
else
[]
end
end
end
class Journal
attr_reader :journal
def initialize(journal)
@journal = journal
end
def user_id
journal.fetch("user", {}).fetch("id")
end
def user_name
journal.fetch("user", {}).fetch("name")
end
def created_on
journal.fetch("created_on")
end
def details
journal.fetch("details", [])
end
def notes
journal.fetch("notes", "")
end
def private_notes?
journal.fetch("private_notes", false)
end
end
end

View File

@ -2,7 +2,7 @@ require 'tempfile'
class TextileToMarkdown
# https://github.com/Ecodev/redmine_convert_textile_to_markown
def self.convert(textile)
def self.call(textile)
# Redmine support @ inside inline code marked with @ (such as "@git@github.com@"), but not pandoc.
# So we inject a placeholder that will be replaced later on with a real backtick.
tag_code = 'pandoc-unescaped-single-backtick'