Patch by Mizuki ISHIKAWA. git-svn-id: http://svn.redmine.org/redmine/trunk@21252 e93f8b46-1217-0410-a6f0-8f06a7374b81tags/5.0.0
@@ -1753,6 +1753,7 @@ Style/SoleNestedConditional: | |||
- 'app/models/version.rb' | |||
- 'app/models/watcher.rb' | |||
- 'app/models/wiki_page.rb' | |||
- 'app/models/wiki_annotate.rb' | |||
- 'lib/redmine/core_ext/active_record.rb' | |||
# Cop supports --auto-correct. |
@@ -0,0 +1,69 @@ | |||
# frozen_string_literal: true | |||
# Redmine - project management software | |||
# Copyright (C) 2006-2021 Jean-Philippe Lang | |||
# | |||
# This program is free software; you can redistribute it and/or | |||
# modify it under the terms of the GNU General Public License | |||
# as published by the Free Software Foundation; either version 2 | |||
# of the License, or (at your option) any later version. | |||
# | |||
# This program is distributed in the hope that it will be useful, | |||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
# GNU General Public License for more details. | |||
# | |||
# You should have received a copy of the GNU General Public License | |||
# along with this program; if not, write to the Free Software | |||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
class AnonymousUser < User | |||
validate :validate_anonymous_uniqueness, :on => :create | |||
self.valid_statuses = [STATUS_ANONYMOUS] | |||
def validate_anonymous_uniqueness | |||
# There should be only one AnonymousUser in the database | |||
errors.add :base, 'An anonymous user already exists.' if AnonymousUser.unscoped.exists? | |||
end | |||
def available_custom_fields | |||
[] | |||
end | |||
# Overrides a few properties | |||
def logged?; false end | |||
def admin; false end | |||
def name(*args); I18n.t(:label_user_anonymous) end | |||
def mail=(*args); nil end | |||
def mail; nil end | |||
def time_zone; nil end | |||
def rss_key; nil end | |||
def pref | |||
UserPreference.new(:user => self) | |||
end | |||
# Returns the user's bult-in role | |||
def builtin_role | |||
@builtin_role ||= Role.anonymous | |||
end | |||
def membership(*args) | |||
nil | |||
end | |||
def member_of?(*args) | |||
false | |||
end | |||
# Anonymous user can not be destroyed | |||
def destroy | |||
false | |||
end | |||
protected | |||
def instantiate_email_address | |||
end | |||
end |
@@ -999,54 +999,3 @@ class User < Principal | |||
end | |||
end | |||
end | |||
class AnonymousUser < User | |||
validate :validate_anonymous_uniqueness, :on => :create | |||
self.valid_statuses = [STATUS_ANONYMOUS] | |||
def validate_anonymous_uniqueness | |||
# There should be only one AnonymousUser in the database | |||
errors.add :base, 'An anonymous user already exists.' if AnonymousUser.unscoped.exists? | |||
end | |||
def available_custom_fields | |||
[] | |||
end | |||
# Overrides a few properties | |||
def logged?; false end | |||
def admin; false end | |||
def name(*args); I18n.t(:label_user_anonymous) end | |||
def mail=(*args); nil end | |||
def mail; nil end | |||
def time_zone; nil end | |||
def rss_key; nil end | |||
def pref | |||
UserPreference.new(:user => self) | |||
end | |||
# Returns the user's bult-in role | |||
def builtin_role | |||
@builtin_role ||= Role.anonymous | |||
end | |||
def membership(*args) | |||
nil | |||
end | |||
def member_of?(*args) | |||
false | |||
end | |||
# Anonymous user can not be destroyed | |||
def destroy | |||
false | |||
end | |||
protected | |||
def instantiate_email_address | |||
end | |||
end |
@@ -0,0 +1,58 @@ | |||
# frozen_string_literal: true | |||
# Redmine - project management software | |||
# Copyright (C) 2006-2021 Jean-Philippe Lang | |||
# | |||
# This program is free software; you can redistribute it and/or | |||
# modify it under the terms of the GNU General Public License | |||
# as published by the Free Software Foundation; either version 2 | |||
# of the License, or (at your option) any later version. | |||
# | |||
# This program is distributed in the hope that it will be useful, | |||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
# GNU General Public License for more details. | |||
# | |||
# You should have received a copy of the GNU General Public License | |||
# along with this program; if not, write to the Free Software | |||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
class WikiAnnotate | |||
attr_reader :lines, :content | |||
def initialize(content) | |||
@content = content | |||
current = content | |||
current_lines = current.text.split(/\r?\n/) | |||
@lines = current_lines.collect {|t| [nil, nil, t]} | |||
positions = [] | |||
current_lines.size.times {|i| positions << i} | |||
while current.previous | |||
d = current.previous.text.split(/\r?\n/).diff(current.text.split(/\r?\n/)).diffs.flatten | |||
d.each_slice(3) do |s| | |||
sign, line = s[0], s[1] | |||
if sign == '+' && positions[line] && positions[line] != -1 | |||
if @lines[positions[line]][0].nil? | |||
@lines[positions[line]][0] = current.version | |||
@lines[positions[line]][1] = current.author | |||
end | |||
end | |||
if sign == '-' | |||
positions.insert(line, -1) | |||
else | |||
positions[line] = nil | |||
end | |||
end | |||
positions.compact! | |||
# Stop if every line is annotated | |||
break unless @lines.detect {|line| line[0].nil?} | |||
current = current.previous | |||
end | |||
@lines.each do |line| | |||
line[0] ||= current.version | |||
# if the last known version is > 1 (eg. history was cleared), we don't know the author | |||
line[1] ||= current.author if current.version == 1 | |||
end | |||
end | |||
end |
@@ -0,0 +1,28 @@ | |||
# frozen_string_literal: true | |||
# Redmine - project management software | |||
# Copyright (C) 2006-2021 Jean-Philippe Lang | |||
# | |||
# This program is free software; you can redistribute it and/or | |||
# modify it under the terms of the GNU General Public License | |||
# as published by the Free Software Foundation; either version 2 | |||
# of the License, or (at your option) any later version. | |||
# | |||
# This program is distributed in the hope that it will be useful, | |||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
# GNU General Public License for more details. | |||
# | |||
# You should have received a copy of the GNU General Public License | |||
# along with this program; if not, write to the Free Software | |||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
class WikiDiff < Redmine::Helpers::Diff | |||
attr_reader :content_to, :content_from | |||
def initialize(content_to, content_from) | |||
@content_to = content_to | |||
@content_from = content_from | |||
super(content_to.text, content_from.text) | |||
end | |||
end |
@@ -290,53 +290,3 @@ class WikiPage < ActiveRecord::Base | |||
(association(:content).loaded? ? content : content_without_text).try(name) | |||
end | |||
end | |||
class WikiDiff < Redmine::Helpers::Diff | |||
attr_reader :content_to, :content_from | |||
def initialize(content_to, content_from) | |||
@content_to = content_to | |||
@content_from = content_from | |||
super(content_to.text, content_from.text) | |||
end | |||
end | |||
class WikiAnnotate | |||
attr_reader :lines, :content | |||
def initialize(content) | |||
@content = content | |||
current = content | |||
current_lines = current.text.split(/\r?\n/) | |||
@lines = current_lines.collect {|t| [nil, nil, t]} | |||
positions = [] | |||
current_lines.size.times {|i| positions << i} | |||
while current.previous | |||
d = current.previous.text.split(/\r?\n/).diff(current.text.split(/\r?\n/)).diffs.flatten | |||
d.each_slice(3) do |s| | |||
sign, line = s[0], s[1] | |||
if sign == '+' && positions[line] && positions[line] != -1 | |||
if @lines[positions[line]][0].nil? | |||
@lines[positions[line]][0] = current.version | |||
@lines[positions[line]][1] = current.author | |||
end | |||
end | |||
if sign == '-' | |||
positions.insert(line, -1) | |||
else | |||
positions[line] = nil | |||
end | |||
end | |||
positions.compact! | |||
# Stop if every line is annotated | |||
break unless @lines.detect {|line| line[0].nil?} | |||
current = current.previous | |||
end | |||
@lines.each do |line| | |||
line[0] ||= current.version | |||
# if the last known version is > 1 (eg. history was cleared), we don't know the author | |||
line[1] ||= current.author if current.version == 1 | |||
end | |||
end | |||
end |
@@ -58,8 +58,10 @@ require 'redmine/thumbnail' | |||
require 'redmine/unified_diff' | |||
require 'redmine/utils' | |||
require 'redmine/version' | |||
require 'redmine/wiki_formatting/links_helper' | |||
require 'redmine/wiki_formatting' | |||
require 'redmine/default_data/loader' | |||
require 'redmine/helpers/calendar' | |||
require 'redmine/helpers/diff' |
@@ -115,89 +115,6 @@ module Redmine | |||
end | |||
end | |||
module LinksHelper | |||
AUTO_LINK_RE = %r{ | |||
( # leading text | |||
<\w+[^>]*?>| # leading HTML tag, or | |||
[\s\(\[,;]| # leading punctuation, or | |||
^ # beginning of line | |||
) | |||
( | |||
(?:https?://)| # protocol spec, or | |||
(?:s?ftps?://)| | |||
(?:www\.) # www.* | |||
) | |||
( | |||
([^<]\S*?) # url | |||
(\/)? # slash | |||
) | |||
((?:>)?|[^[:alnum:]_\=\/;\(\)\-]*?) # post | |||
(?=<|\s|$) | |||
}x unless const_defined?(:AUTO_LINK_RE) | |||
# Destructively replaces urls into clickable links | |||
def auto_link!(text) | |||
text.gsub!(AUTO_LINK_RE) do | |||
all, leading, proto, url, post = $&, $1, $2, $3, $6 | |||
if /<a\s/i.match?(leading) || /![<>=]?/.match?(leading) | |||
# don't replace URLs that are already linked | |||
# and URLs prefixed with ! !> !< != (textile images) | |||
all | |||
else | |||
# Idea below : an URL with unbalanced parenthesis and | |||
# ending by ')' is put into external parenthesis | |||
if url[-1] == ")" and ((url.count("(") - url.count(")")) < 0) | |||
url = url[0..-2] # discard closing parenthesis from url | |||
post = ")" + post # add closing parenthesis to post | |||
end | |||
content = proto + url | |||
href = "#{proto=="www."?"http://www.":proto}#{url}" | |||
%(#{leading}<a class="external" href="#{ERB::Util.html_escape href}">#{ERB::Util.html_escape content}</a>#{post}).html_safe | |||
end | |||
end | |||
end | |||
# Destructively replaces email addresses into clickable links | |||
def auto_mailto!(text) | |||
text.gsub!(/([\w\.!#\$%\-+.\/]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do | |||
mail = $1 | |||
if /<a\b[^>]*>(.*)(#{Regexp.escape(mail)})(.*)<\/a>/.match?(text) | |||
else | |||
%(<a class="email" href="mailto:#{ERB::Util.html_escape mail}">#{ERB::Util.html_escape mail}</a>).html_safe | |||
end | |||
end | |||
end | |||
def restore_redmine_links(html) | |||
# restore wiki links eg. [[Foo]] | |||
html.gsub!(%r{\[<a href="(.*?)">(.*?)</a>\]}) do | |||
"[[#{$2}]]" | |||
end | |||
# restore Redmine links with double-quotes, eg. version:"1.0" | |||
html.gsub!(/(\w):"(.+?)"/) do | |||
"#{$1}:\"#{$2}\"" | |||
end | |||
# restore user links with @ in login name eg. [@jsmith@somenet.foo] | |||
html.gsub!(%r{[@\A]<a(\sclass="email")? href="mailto:(.*?)">(.*?)</a>}) do | |||
"@#{$2}" | |||
end | |||
# restore user links with @ in login name eg. [user:jsmith@somenet.foo] | |||
html.gsub!(%r{\buser:<a(\sclass="email")? href="mailto:(.*?)">(.*?)<\/a>}) do | |||
"user:#{$2}" | |||
end | |||
# restore attachments links with @ in file name eg. [attachment:image@2x.png] | |||
html.gsub!(%r{\battachment:<a(\sclass="email")? href="mailto:(.*?)">(.*?)</a>}) do | |||
"attachment:#{$2}" | |||
end | |||
# restore hires images which are misrecognized as email address eg. [printscreen@2x.png] | |||
html.gsub!(%r{<a(\sclass="email")? href="mailto:[^"]+@\dx\.(bmp|gif|jpg|jpe|jpeg|png)">(.*?)</a>}) do | |||
"#{$3}" | |||
end | |||
html | |||
end | |||
end | |||
# Default formatter module | |||
module NullFormatter | |||
class Formatter |
@@ -0,0 +1,107 @@ | |||
# frozen_string_literal: true | |||
# Redmine - project management software | |||
# Copyright (C) 2006-2021 Jean-Philippe Lang | |||
# | |||
# This program is free software; you can redistribute it and/or | |||
# modify it under the terms of the GNU General Public License | |||
# as published by the Free Software Foundation; either version 2 | |||
# of the License, or (at your option) any later version. | |||
# | |||
# This program is distributed in the hope that it will be useful, | |||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
# GNU General Public License for more details. | |||
# | |||
# You should have received a copy of the GNU General Public License | |||
# along with this program; if not, write to the Free Software | |||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
require 'loofah/helpers' | |||
module Redmine | |||
module WikiFormatting | |||
module LinksHelper | |||
AUTO_LINK_RE = %r{ | |||
( # leading text | |||
<\w+[^>]*?>| # leading HTML tag, or | |||
[\s\(\[,;]| # leading punctuation, or | |||
^ # beginning of line | |||
) | |||
( | |||
(?:https?://)| # protocol spec, or | |||
(?:s?ftps?://)| | |||
(?:www\.) # www.* | |||
) | |||
( | |||
([^<]\S*?) # url | |||
(\/)? # slash | |||
) | |||
((?:>)?|[^[:alnum:]_\=\/;\(\)\-]*?) # post | |||
(?=<|\s|$) | |||
}x unless const_defined?(:AUTO_LINK_RE) | |||
# Destructively replaces urls into clickable links | |||
def auto_link!(text) | |||
text.gsub!(AUTO_LINK_RE) do | |||
all, leading, proto, url, post = $&, $1, $2, $3, $6 | |||
if /<a\s/i.match?(leading) || /![<>=]?/.match?(leading) | |||
# don't replace URLs that are already linked | |||
# and URLs prefixed with ! !> !< != (textile images) | |||
all | |||
else | |||
# Idea below : an URL with unbalanced parenthesis and | |||
# ending by ')' is put into external parenthesis | |||
if url[-1] == ")" and ((url.count("(") - url.count(")")) < 0) | |||
url = url[0..-2] # discard closing parenthesis from url | |||
post = ")" + post # add closing parenthesis to post | |||
end | |||
content = proto + url | |||
href = "#{proto=="www."?"http://www.":proto}#{url}" | |||
%(#{leading}<a class="external" href="#{ERB::Util.html_escape href}">#{ERB::Util.html_escape content}</a>#{post}).html_safe | |||
end | |||
end | |||
end | |||
# Destructively replaces email addresses into clickable links | |||
def auto_mailto!(text) | |||
text.gsub!(/([\w\.!#\$%\-+.\/]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do | |||
mail = $1 | |||
if /<a\b[^>]*>(.*)(#{Regexp.escape(mail)})(.*)<\/a>/.match?(text) | |||
else | |||
%(<a class="email" href="mailto:#{ERB::Util.html_escape mail}">#{ERB::Util.html_escape mail}</a>).html_safe | |||
end | |||
end | |||
end | |||
def restore_redmine_links(html) | |||
# restore wiki links eg. [[Foo]] | |||
html.gsub!(%r{\[<a href="(.*?)">(.*?)</a>\]}) do | |||
"[[#{$2}]]" | |||
end | |||
# restore Redmine links with double-quotes, eg. version:"1.0" | |||
html.gsub!(/(\w):"(.+?)"/) do | |||
"#{$1}:\"#{$2}\"" | |||
end | |||
# restore user links with @ in login name eg. [@jsmith@somenet.foo] | |||
html.gsub!(%r{[@\A]<a(\sclass="email")? href="mailto:(.*?)">(.*?)</a>}) do | |||
"@#{$2}" | |||
end | |||
# restore user links with @ in login name eg. [user:jsmith@somenet.foo] | |||
html.gsub!(%r{\buser:<a(\sclass="email")? href="mailto:(.*?)">(.*?)<\/a>}) do | |||
"user:#{$2}" | |||
end | |||
# restore attachments links with @ in file name eg. [attachment:image@2x.png] | |||
html.gsub!(%r{\battachment:<a(\sclass="email")? href="mailto:(.*?)">(.*?)</a>}) do | |||
"attachment:#{$2}" | |||
end | |||
# restore hires images which are misrecognized as email address eg. [printscreen@2x.png] | |||
html.gsub!(%r{<a(\sclass="email")? href="mailto:[^"]+@\dx\.(bmp|gif|jpg|jpe|jpeg|png)">(.*?)</a>}) do | |||
"#{$3}" | |||
end | |||
html | |||
end | |||
end | |||
end | |||
end |