Actualiza a Redmine 3.4.13

This commit is contained in:
Manuel Cillero 2020-07-03 21:39:03 +02:00
parent 807ff3308d
commit ecddcaf1d3
224 changed files with 2222 additions and 1000 deletions

20
Gemfile
View file

@ -1,10 +1,8 @@
source 'https://rubygems.org' source 'https://rubygems.org'
if Gem::Version.new(Bundler::VERSION) < Gem::Version.new('1.5.0') gem "bundler", ">= 1.5.0", "< 2.0.0"
abort "Redmine requires Bundler 1.5.0 or higher (you're using #{Bundler::VERSION}).\nPlease update with 'gem update bundler'."
end
gem "rails", "4.2.8" gem "rails", "4.2.11.1"
gem "addressable", "2.4.0" if RUBY_VERSION < "2.0" gem "addressable", "2.4.0" if RUBY_VERSION < "2.0"
if RUBY_VERSION < "2.1" if RUBY_VERSION < "2.1"
gem "public_suffix", (RUBY_VERSION < "2.0" ? "~> 1.4" : "~> 2.0.5") gem "public_suffix", (RUBY_VERSION < "2.0" ? "~> 1.4" : "~> 2.0.5")
@ -23,13 +21,17 @@ gem "mail", "~> 2.6.4"
gem "nokogiri", (RUBY_VERSION >= "2.1" ? "~> 1.8.1" : "~> 1.6.8") gem "nokogiri", (RUBY_VERSION >= "2.1" ? "~> 1.8.1" : "~> 1.6.8")
gem "i18n", "~> 0.7.0" gem "i18n", "~> 0.7.0"
gem "ffi", "1.9.14", :platforms => :mingw if RUBY_VERSION < "2.0" gem "ffi", "1.9.14", :platforms => :mingw if RUBY_VERSION < "2.0"
gem "xpath", "< 3.2.0" if RUBY_VERSION < "2.3"
# Request at least rails-html-sanitizer 1.0.3 because of security advisories # Request at least rails-html-sanitizer 1.0.3 because of security advisories
gem "rails-html-sanitizer", ">= 1.0.3" gem "rails-html-sanitizer", ">= 1.0.3"
# TODO: Remove the following line when #32223 is fixed
gem "sprockets", "~> 3.7.2"
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :x64_mingw, :mswin] gem 'tzinfo-data', platforms: [:mingw, :x64_mingw, :mswin]
gem "rbpdf", "~> 1.19.3" gem "rbpdf", "~> 1.19.6"
# Optional gem for LDAP authentication # Optional gem for LDAP authentication
group :ldap do group :ldap do
@ -38,14 +40,14 @@ end
# Optional gem for OpenID authentication # Optional gem for OpenID authentication
group :openid do group :openid do
gem "ruby-openid", "~> 2.3.0", :require => "openid" gem "ruby-openid", "~> 2.9.2", :require => "openid"
gem "rack-openid" gem "rack-openid"
end end
platforms :mri, :mingw, :x64_mingw do platforms :mri, :mingw, :x64_mingw do
# Optional gem for exporting the gantt to a PNG file, not supported with jruby # Optional gem for exporting the gantt to a PNG file, not supported with jruby
group :rmagick do group :rmagick do
gem "rmagick", ">= 2.14.0" gem "rmagick", "~> 2.16.0"
end end
# Optional Markdown support, not for JRuby # Optional Markdown support, not for JRuby
@ -94,12 +96,12 @@ end
group :test do group :test do
gem "minitest" gem "minitest"
gem "rails-dom-testing" gem "rails-dom-testing"
gem "mocha" gem 'mocha', '>= 1.4.0'
gem "simplecov", "~> 0.9.1", :require => false gem "simplecov", "~> 0.9.1", :require => false
# TODO: remove this after upgrading to Rails 5 # TODO: remove this after upgrading to Rails 5
gem "test_after_commit", "~> 0.4.2" gem "test_after_commit", "~> 0.4.2"
# For running UI tests # For running UI tests
gem "capybara" gem "capybara", '~> 2.13'
gem "selenium-webdriver", "~> 2.53.4" gem "selenium-webdriver", "~> 2.53.4"
end end

View file

@ -87,7 +87,7 @@ class AccountController < ApplicationController
@user.must_change_passwd = false @user.must_change_passwd = false
if @user.save if @user.save
@token.destroy @token.destroy
Mailer.password_updated(@user) Mailer.password_updated(@user, { remote_ip: request.remote_ip })
flash[:notice] = l(:notice_account_password_updated) flash[:notice] = l(:notice_account_password_updated)
redirect_to signin_path redirect_to signin_path
return return

View file

@ -60,7 +60,7 @@ class AttachmentsController < ApplicationController
@attachment.increment_download @attachment.increment_download
end end
if stale?(:etag => @attachment.digest) if stale?(:etag => @attachment.digest, :template => false)
# images are sent inline # images are sent inline
send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename), send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
:type => detect_content_type(@attachment), :type => detect_content_type(@attachment),
@ -70,7 +70,7 @@ class AttachmentsController < ApplicationController
def thumbnail def thumbnail
if @attachment.thumbnailable? && tbnail = @attachment.thumbnail(:size => params[:size]) if @attachment.thumbnailable? && tbnail = @attachment.thumbnail(:size => params[:size])
if stale?(:etag => tbnail) if stale?(:etag => tbnail, :template => false)
send_file tbnail, send_file tbnail,
:filename => filename_for_content_disposition(@attachment.filename), :filename => filename_for_content_disposition(@attachment.filename),
:type => detect_content_type(@attachment), :type => detect_content_type(@attachment),

View file

@ -19,7 +19,7 @@ class AutoCompletesController < ApplicationController
before_action :find_project before_action :find_project
def issues def issues
@issues = [] issues = []
q = (params[:q] || params[:term]).to_s.strip q = (params[:q] || params[:term]).to_s.strip
status = params[:status].to_s status = params[:status].to_s
issue_id = params[:issue_id].to_s issue_id = params[:issue_id].to_s
@ -32,13 +32,14 @@ class AutoCompletesController < ApplicationController
scope = scope.where.not(:id => issue_id.to_i) scope = scope.where.not(:id => issue_id.to_i)
end end
if q.match(/\A#?(\d+)\z/) if q.match(/\A#?(\d+)\z/)
@issues << scope.find_by_id($1.to_i) issues << scope.find_by_id($1.to_i)
end end
@issues += scope.like(q).order(:id => :desc).limit(10).to_a issues += scope.like(q).order(:id => :desc).limit(10).to_a
@issues.compact! issues.compact!
end end
render :layout => false
render :json => format_issues_json(issues)
end end
private private
@ -50,4 +51,13 @@ class AutoCompletesController < ApplicationController
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
render_404 render_404
end end
def format_issues_json(issues)
issues.map {|issue| {
'id' => issue.id,
'label' => "#{issue.tracker} ##{issue.id}: #{issue.subject.to_s.truncate(60)}",
'value' => issue.id
}
}
end
end end

View file

@ -91,8 +91,10 @@ class EnumerationsController < ApplicationController
def build_new_enumeration def build_new_enumeration
class_name = params[:enumeration] && params[:enumeration][:type] || params[:type] class_name = params[:enumeration] && params[:enumeration][:type] || params[:type]
@enumeration = Enumeration.new_subclass_instance(class_name, enumeration_params) @enumeration = Enumeration.new_subclass_instance(class_name)
if @enumeration.nil? if @enumeration
@enumeration.attributes = enumeration_params || {}
else
render_404 render_404
end end
end end
@ -105,6 +107,7 @@ class EnumerationsController < ApplicationController
def enumeration_params def enumeration_params
# can't require enumeration on #new action # can't require enumeration on #new action
params.permit(:enumeration => [:name, :active, :is_default, :position])[:enumeration] cf_ids = @enumeration.available_custom_fields.map{|c| c.id.to_s}
params.permit(:enumeration => [:name, :active, :is_default, :position, :custom_field_values => cf_ids])[:enumeration]
end end
end end

View file

@ -40,7 +40,8 @@ class IssuesController < ApplicationController
helper :timelog helper :timelog
def index def index
retrieve_query use_session = !request.format.csv?
retrieve_query(IssueQuery, use_session)
if @query.valid? if @query.valid?
respond_to do |format| respond_to do |format|
@ -367,7 +368,12 @@ class IssuesController < ApplicationController
when 'destroy' when 'destroy'
# nothing to do # nothing to do
when 'nullify' when 'nullify'
if Setting.timelog_required_fields.include?('issue_id')
flash.now[:error] = l(:field_issue) + " " + ::I18n.t('activerecord.errors.messages.blank')
return
else
time_entries.update_all(:issue_id => nil) time_entries.update_all(:issue_id => nil)
end
when 'reassign' when 'reassign'
reassign_to = @project && @project.issues.find_by_id(params[:reassign_to_id]) reassign_to = @project && @project.issues.find_by_id(params[:reassign_to_id])
if reassign_to.nil? if reassign_to.nil?

View file

@ -37,7 +37,7 @@ class SearchController < ApplicationController
end end
# quick jump to an issue # quick jump to an issue
if (m = @question.match(/^#?(\d+)$/)) && (issue = Issue.visible.find_by_id(m[1].to_i)) if !api_request? && (m = @question.match(/^#?(\d+)$/)) && (issue = Issue.visible.find_by_id(m[1].to_i))
redirect_to issue_path(issue) redirect_to issue_path(issue)
return return
end end
@ -49,7 +49,7 @@ class SearchController < ApplicationController
when 'my_projects' when 'my_projects'
User.current.projects User.current.projects
when 'subprojects' when 'subprojects'
@project ? (@project.self_and_descendants.active.to_a) : nil @project ? (@project.self_and_descendants.to_a) : nil
else else
@project @project
end end

View file

@ -114,6 +114,7 @@ class TimelogController < ApplicationController
:time_entry => { :time_entry => {
:project_id => params[:time_entry][:project_id], :project_id => params[:time_entry][:project_id],
:issue_id => @time_entry.issue_id, :issue_id => @time_entry.issue_id,
:spent_on => @time_entry.spent_on,
:activity_id => @time_entry.activity_id :activity_id => @time_entry.activity_id
}, },
:back_url => params[:back_url] :back_url => params[:back_url]

View file

@ -106,6 +106,6 @@ class TrackersController < ApplicationController
return return
end end
@trackers = Tracker.sorted.to_a @trackers = Tracker.sorted.to_a
@custom_fields = IssueCustomField.all.sort @custom_fields = IssueCustomField.sorted
end end
end end

View file

@ -32,7 +32,7 @@
class WikiController < ApplicationController class WikiController < ApplicationController
default_search_scope :wiki_pages default_search_scope :wiki_pages
before_action :find_wiki, :authorize before_action :find_wiki, :authorize
before_action :find_existing_or_new_page, :only => [:show, :edit, :update] before_action :find_existing_or_new_page, :only => [:show, :edit]
before_action :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy, :destroy_version] before_action :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy, :destroy_version]
before_action :find_attachments, :only => [:preview] before_action :find_attachments, :only => [:preview]
accept_api_auth :index, :show, :update, :destroy accept_api_auth :index, :show, :update, :destroy
@ -42,8 +42,6 @@ class WikiController < ApplicationController
helper :watchers helper :watchers
include Redmine::Export::PDF include Redmine::Export::PDF
include ActionView::Helpers::SanitizeHelper
# List of pages, sorted alphabetically and by parent (hierarchy) # List of pages, sorted alphabetically and by parent (hierarchy)
def index def index
load_pages_for_index load_pages_for_index
@ -109,7 +107,7 @@ class WikiController < ApplicationController
send_data(export, :type => 'text/html', :filename => filename_for_content_disposition("#{@page.title}.html")) send_data(export, :type => 'text/html', :filename => filename_for_content_disposition("#{@page.title}.html"))
return return
elsif params[:format] == 'txt' elsif params[:format] == 'txt'
send_data(strip_tags(@content.text), :type => 'text/plain', :filename => filename_for_content_disposition("#{@page.title}.txt")) send_data(@content.text, :type => 'text/plain', :filename => filename_for_content_disposition("#{@page.title}.txt"))
return return
end end
end end
@ -152,6 +150,8 @@ class WikiController < ApplicationController
# Creates a new page or updates an existing one # Creates a new page or updates an existing one
def update def update
@page = @wiki.find_or_new_page(params[:id])
return render_403 unless editable? return render_403 unless editable?
was_new_page = @page.new_record? was_new_page = @page.new_record?
@page.safe_attributes = params[:wiki_page] @page.safe_attributes = params[:wiki_page]

View file

@ -1272,7 +1272,7 @@ module ApplicationHelper
link_to_function '', link_to_function '',
"toggleCheckboxesBySelector('#{selector}')", "toggleCheckboxesBySelector('#{selector}')",
:title => "#{l(:button_check_all)} / #{l(:button_uncheck_all)}", :title => "#{l(:button_check_all)} / #{l(:button_uncheck_all)}",
:class => 'toggle-checkboxes' :class => 'icon icon-checked'
end end
def progress_bar(pcts, options={}) def progress_bar(pcts, options={})
@ -1423,10 +1423,13 @@ module ApplicationHelper
end end
if email.present? if email.present?
gravatar(email.to_s.downcase, options) rescue nil gravatar(email.to_s.downcase, options) rescue nil
else elsif user.is_a?(AnonymousUser)
options[:size] &&= options[:size].to_s
image_tag 'anonymous.png', image_tag 'anonymous.png',
GravatarHelper::DEFAULT_OPTIONS GravatarHelper::DEFAULT_OPTIONS
.except(:default, :rating, :ssl).merge(options) .except(:default, :rating, :ssl).merge(options)
else
nil
end end
else else
'' ''

View file

@ -127,8 +127,8 @@ module IssuesHelper
content_tag('td', check_box_tag("ids[]", other_issue.id, false, :id => nil), :class => 'checkbox') + content_tag('td', check_box_tag("ids[]", other_issue.id, false, :id => nil), :class => 'checkbox') +
content_tag('td', relation.to_s(@issue) {|other| link_to_issue(other, :project => Setting.cross_project_issue_relations?)}.html_safe, :class => 'subject', :style => 'width: 50%') + content_tag('td', relation.to_s(@issue) {|other| link_to_issue(other, :project => Setting.cross_project_issue_relations?)}.html_safe, :class => 'subject', :style => 'width: 50%') +
content_tag('td', other_issue.status, :class => 'status') + content_tag('td', other_issue.status, :class => 'status') +
content_tag('td', other_issue.start_date, :class => 'start_date') + content_tag('td', format_date(other_issue.start_date), :class => 'start_date') +
content_tag('td', other_issue.due_date, :class => 'due_date') + content_tag('td', format_date(other_issue.due_date), :class => 'due_date') +
content_tag('td', other_issue.disabled_core_fields.include?('done_ratio') ? '' : progress_bar(other_issue.done_ratio), :class=> 'done_ratio') + content_tag('td', other_issue.disabled_core_fields.include?('done_ratio') ? '' : progress_bar(other_issue.done_ratio), :class=> 'done_ratio') +
content_tag('td', link, :class => 'buttons'), content_tag('td', link, :class => 'buttons'),
:id => "relation-#{relation.id}", :id => "relation-#{relation.id}",
@ -246,8 +246,12 @@ module IssuesHelper
issue_fields_rows do |rows| issue_fields_rows do |rows|
values.each_with_index do |value, i| values.each_with_index do |value, i|
css = "cf_#{value.custom_field.id}" css = "cf_#{value.custom_field.id}"
attr_value = show_value(value)
if value.custom_field.text_formatting == 'full'
attr_value = content_tag('div', attr_value, class: 'wiki')
end
m = (i < half ? :left : :right) m = (i < half ? :left : :right)
rows.send m, custom_field_name_tag(value.custom_field), show_value(value), :class => css rows.send m, custom_field_name_tag(value.custom_field), attr_value, :class => css
end end
end end
end end
@ -310,7 +314,7 @@ module IssuesHelper
# Returns an array of users that are proposed as watchers # Returns an array of users that are proposed as watchers
# on the new issue form # on the new issue form
def users_for_new_issue_watchers(issue) def users_for_new_issue_watchers(issue)
users = issue.watcher_users users = issue.watcher_users.select{|u| u.status == User::STATUS_ACTIVE}
if issue.project.users.count <= 20 if issue.project.users.count <= 20
users = (users + issue.project.users.sort).uniq users = (users + issue.project.users.sort).uniq
end end

View file

@ -18,14 +18,11 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module SearchHelper module SearchHelper
include ActionView::Helpers::SanitizeHelper
def highlight_tokens(text, tokens) def highlight_tokens(text, tokens)
return text unless text && tokens && !tokens.empty? return text unless text && tokens && !tokens.empty?
re_tokens = tokens.collect {|t| Regexp.escape(t)} re_tokens = tokens.collect {|t| Regexp.escape(t)}
regexp = Regexp.new "(#{re_tokens.join('|')})", Regexp::IGNORECASE regexp = Regexp.new "(#{re_tokens.join('|')})", Regexp::IGNORECASE
result = '' result = ''
text = strip_tags(text)
text.split(regexp).each_with_index do |words, i| text.split(regexp).each_with_index do |words, i|
if result.length > 1200 if result.length > 1200
# maximum length of the preview reached # maximum length of the preview reached

View file

@ -156,7 +156,7 @@ class Attachment < ActiveRecord::Base
end end
def title def title
title = filename.to_s title = filename.dup
if description.present? if description.present?
title << " (#{description})" title << " (#{description})"
end end

View file

@ -217,6 +217,7 @@ class Import < ActiveRecord::Base
csv_options = {:headers => false} csv_options = {:headers => false}
csv_options[:encoding] = settings['encoding'].to_s.presence || 'UTF-8' csv_options[:encoding] = settings['encoding'].to_s.presence || 'UTF-8'
csv_options[:encoding] = 'bom|UTF-8' if csv_options[:encoding] == 'UTF-8'
separator = settings['separator'].to_s separator = settings['separator'].to_s
csv_options[:col_sep] = separator if separator.size == 1 csv_options[:col_sep] = separator if separator.size == 1
wrapper = settings['wrapper'].to_s wrapper = settings['wrapper'].to_s

View file

@ -275,7 +275,8 @@ class Issue < ActiveRecord::Base
end end
end end
unless options[:watchers] == false unless options[:watchers] == false
self.watcher_user_ids = issue.watcher_user_ids.dup self.watcher_user_ids =
issue.watcher_users.select{|u| u.status == User::STATUS_ACTIVE}.map(&:id)
end end
@copied_from = issue @copied_from = issue
@copy_options = options @copy_options = options
@ -1086,7 +1087,7 @@ class Issue < ActiveRecord::Base
if leaf? if leaf?
estimated_hours estimated_hours
else else
@total_estimated_hours ||= self_and_descendants.sum(:estimated_hours) @total_estimated_hours ||= self_and_descendants.visible.sum(:estimated_hours)
end end
end end
@ -1300,7 +1301,7 @@ class Issue < ActiveRecord::Base
# Reschedules the issue on the given date or the next working day and saves the record. # Reschedules the issue on the given date or the next working day and saves the record.
# If the issue is a parent task, this is done by rescheduling its subtasks. # If the issue is a parent task, this is done by rescheduling its subtasks.
def reschedule_on!(date) def reschedule_on!(date, journal=nil)
return if date.nil? return if date.nil?
if leaf? || !dates_derived? if leaf? || !dates_derived?
if start_date.nil? || start_date != date if start_date.nil? || start_date != date
@ -1308,6 +1309,9 @@ class Issue < ActiveRecord::Base
# Issue can not be moved earlier than its soonest start date # Issue can not be moved earlier than its soonest start date
date = [soonest_start(true), date].compact.max date = [soonest_start(true), date].compact.max
end end
if journal
init_journal(journal.user)
end
reschedule_on(date) reschedule_on(date)
begin begin
save save
@ -1631,6 +1635,8 @@ class Issue < ActiveRecord::Base
copy.author = author copy.author = author
copy.project = project copy.project = project
copy.parent_issue_id = copied_issue_ids[child.parent_id] copy.parent_issue_id = copied_issue_ids[child.parent_id]
copy.fixed_version_id = nil unless child.fixed_version.present? && child.fixed_version.status == 'open'
copy.assigned_to = nil unless child.assigned_to_id.present? && child.assigned_to.status == User::STATUS_ACTIVE
unless copy.save unless copy.save
logger.error "Could not copy subtask ##{child.id} while copying ##{@copied_from.id} to ##{id} due to validation errors: #{copy.errors.full_messages.join(', ')}" if logger logger.error "Could not copy subtask ##{child.id} while copying ##{@copied_from.id} to ##{id} due to validation errors: #{copy.errors.full_messages.join(', ')}" if logger
next next
@ -1789,7 +1795,7 @@ class Issue < ActiveRecord::Base
def reschedule_following_issues def reschedule_following_issues
if start_date_changed? || due_date_changed? if start_date_changed? || due_date_changed?
relations_from.each do |relation| relations_from.each do |relation|
relation.set_issue_to_dates relation.set_issue_to_dates(@current_journal)
end end
end end
end end

View file

@ -122,7 +122,10 @@ class IssueImport < Import
end end
end end
if issue.project && version_name = row_value(row, 'fixed_version') if issue.project && version_name = row_value(row, 'fixed_version')
if version = issue.project.versions.named(version_name).first version =
issue.project.versions.named(version_name).first ||
issue.project.shared_versions.named(version_name).first
if version
attributes['fixed_version_id'] = version.id attributes['fixed_version_id'] = version.id
elsif create_versions? elsif create_versions?
version = issue.project.versions.build version = issue.project.versions.build

View file

@ -37,8 +37,8 @@ class IssueQuery < Query
QueryColumn.new(:due_date, :sortable => "#{Issue.table_name}.due_date"), QueryColumn.new(:due_date, :sortable => "#{Issue.table_name}.due_date"),
QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours", :totalable => true), QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours", :totalable => true),
QueryColumn.new(:total_estimated_hours, QueryColumn.new(:total_estimated_hours,
:sortable => "COALESCE((SELECT SUM(estimated_hours) FROM #{Issue.table_name} subtasks" + :sortable => -> { "COALESCE((SELECT SUM(estimated_hours) FROM #{Issue.table_name} subtasks" +
" WHERE subtasks.root_id = #{Issue.table_name}.root_id AND subtasks.lft >= #{Issue.table_name}.lft AND subtasks.rgt <= #{Issue.table_name}.rgt), 0)", " WHERE #{Issue.visible_condition(User.current).gsub(/\bissues\b/, 'subtasks')} AND subtasks.root_id = #{Issue.table_name}.root_id AND subtasks.lft >= #{Issue.table_name}.lft AND subtasks.rgt <= #{Issue.table_name}.rgt), 0)" },
:default_order => 'desc'), :default_order => 'desc'),
QueryColumn.new(:done_ratio, :sortable => "#{Issue.table_name}.done_ratio", :groupable => true), QueryColumn.new(:done_ratio, :sortable => "#{Issue.table_name}.done_ratio", :groupable => true),
QueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on", :default_order => 'desc'), QueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on", :default_order => 'desc'),
@ -221,6 +221,7 @@ class IssueQuery < Query
end end
disabled_fields = Tracker.disabled_core_fields(trackers).map {|field| field.sub(/_id$/, '')} disabled_fields = Tracker.disabled_core_fields(trackers).map {|field| field.sub(/_id$/, '')}
disabled_fields << "total_estimated_hours" if disabled_fields.include?("estimated_hours")
@available_columns.reject! {|column| @available_columns.reject! {|column|
disabled_fields.include?(column.name.to_s) disabled_fields.include?(column.name.to_s)
} }
@ -373,7 +374,7 @@ class IssueQuery < Query
neg = (operator == '!' ? 'NOT' : '') neg = (operator == '!' ? 'NOT' : '')
subquery = "SELECT 1 FROM #{Journal.table_name} sj" + subquery = "SELECT 1 FROM #{Journal.table_name} sj" +
" WHERE sj.journalized_type='Issue' AND sj.journalized_id=#{Issue.table_name}.id AND (#{sql_for_field field, '=', value, 'sj', 'user_id'})" + " WHERE sj.journalized_type='Issue' AND sj.journalized_id=#{Issue.table_name}.id AND (#{sql_for_field field, '=', value, 'sj', 'user_id'})" +
" AND sj.id = (SELECT MAX(#{Journal.table_name}.id) FROM #{Journal.table_name}" + " AND sj.id IN (SELECT MAX(#{Journal.table_name}.id) FROM #{Journal.table_name}" +
" WHERE #{Journal.table_name}.journalized_type='Issue' AND #{Journal.table_name}.journalized_id=#{Issue.table_name}.id" + " WHERE #{Journal.table_name}.journalized_type='Issue' AND #{Journal.table_name}.journalized_id=#{Issue.table_name}.id" +
" AND (#{Journal.visible_notes_condition(User.current, :skip_pre_condition => true)}))" " AND (#{Journal.visible_notes_condition(User.current, :skip_pre_condition => true)}))"
@ -382,8 +383,26 @@ class IssueQuery < Query
def sql_for_watcher_id_field(field, operator, value) def sql_for_watcher_id_field(field, operator, value)
db_table = Watcher.table_name db_table = Watcher.table_name
"#{Issue.table_name}.id #{ operator == '=' ? 'IN' : 'NOT IN' } (SELECT #{db_table}.watchable_id FROM #{db_table} WHERE #{db_table}.watchable_type='Issue' AND " +
sql_for_field(field, '=', value, db_table, 'user_id') + ')' me, others = value.partition { |id| ['0', User.current.id.to_s].include?(id) }
sql = if others.any?
"SELECT #{Issue.table_name}.id FROM #{Issue.table_name} " +
"INNER JOIN #{db_table} ON #{Issue.table_name}.id = #{db_table}.watchable_id AND #{db_table}.watchable_type = 'Issue' " +
"LEFT OUTER JOIN #{Project.table_name} ON #{Project.table_name}.id = #{Issue.table_name}.project_id " +
"WHERE (" +
sql_for_field(field, '=', me, db_table, 'user_id') +
') OR (' +
Project.allowed_to_condition(User.current, :view_issue_watchers) +
' AND ' +
sql_for_field(field, '=', others, db_table, 'user_id') +
')'
else
"SELECT #{db_table}.watchable_id FROM #{db_table} " +
"WHERE #{db_table}.watchable_type='Issue' AND " +
sql_for_field(field, '=', me, db_table, 'user_id')
end
"#{Issue.table_name}.id #{ operator == '=' ? 'IN' : 'NOT IN' } (#{sql})"
end end
def sql_for_member_of_group_field(field, operator, value) def sql_for_member_of_group_field(field, operator, value)

View file

@ -176,10 +176,10 @@ class IssueRelation < ActiveRecord::Base
set_issue_to_dates set_issue_to_dates
end end
def set_issue_to_dates def set_issue_to_dates(journal=nil)
soonest_start = self.successor_soonest_start soonest_start = self.successor_soonest_start
if soonest_start && issue_to if soonest_start && issue_to
issue_to.reschedule_on!(soonest_start) issue_to.reschedule_on!(soonest_start, journal)
end end
end end

View file

@ -54,7 +54,7 @@ class MailHandler < ActionMailer::Base
def self.safe_receive(*args) def self.safe_receive(*args)
receive(*args) receive(*args)
rescue Exception => e rescue Exception => e
logger.error "MailHandler: an unexpected error occurred when receiving email: #{e.message}" if logger Rails.logger.error "MailHandler: an unexpected error occurred when receiving email: #{e.message}"
return false return false
end end
@ -65,7 +65,7 @@ class MailHandler < ActionMailer::Base
%w(project status tracker category priority assigned_to fixed_version).each do |option| %w(project status tracker category priority assigned_to fixed_version).each do |option|
options[:issue][option.to_sym] = env[option] if env[option] options[:issue][option.to_sym] = env[option] if env[option]
end end
%w(allow_override unknown_user no_permission_check no_account_notice default_group project_from_subaddress).each do |option| %w(allow_override unknown_user no_permission_check no_account_notice no_notification default_group project_from_subaddress).each do |option|
options[option.to_sym] = env[option] if env[option] options[option.to_sym] = env[option] if env[option]
end end
if env['private'] if env['private']
@ -250,8 +250,8 @@ class MailHandler < ActionMailer::Base
# add To and Cc as watchers before saving so the watchers can reply to Redmine # add To and Cc as watchers before saving so the watchers can reply to Redmine
add_watchers(issue) add_watchers(issue)
add_attachments(issue)
issue.save! issue.save!
add_attachments(issue)
if logger if logger
logger.info "MailHandler: issue ##{issue.id} updated by #{user}" logger.info "MailHandler: issue ##{issue.id} updated by #{user}"
end end
@ -286,7 +286,7 @@ class MailHandler < ActionMailer::Base
reply reply
else else
if logger if logger
logger.info "MailHandler: ignoring reply from [#{sender_email}] to a locked topic" logger.info "MailHandler: ignoring reply from [#{email.from.first}] to a locked topic"
end end
end end
end end

View file

@ -311,7 +311,7 @@ class Mailer < ActionMailer::Base
end end
# Notifies user that his password was updated # Notifies user that his password was updated
def self.password_updated(user) def self.password_updated(user, options={})
# Don't send a notification to the dummy email address when changing the password # Don't send a notification to the dummy email address when changing the password
# of the default admin account which is required after the first login # of the default admin account which is required after the first login
# TODO: maybe not the best way to handle this # TODO: maybe not the best way to handle this
@ -320,6 +320,8 @@ class Mailer < ActionMailer::Base
security_notification(user, security_notification(user,
message: :mail_body_password_updated, message: :mail_body_password_updated,
title: :button_change_password, title: :button_change_password,
remote_ip: options[:remote_ip],
originator: user,
url: {controller: 'my', action: 'password'} url: {controller: 'my', action: 'password'}
).deliver ).deliver
end end
@ -333,7 +335,6 @@ class Mailer < ActionMailer::Base
end end
def security_notification(recipients, options={}) def security_notification(recipients, options={})
redmine_headers 'Sender' => User.current.login
@user = Array(recipients).detect{|r| r.is_a? User } @user = Array(recipients).detect{|r| r.is_a? User }
set_language_if_valid(@user.try :language) set_language_if_valid(@user.try :language)
@message = l(options[:message], @message = l(options[:message],
@ -341,7 +342,11 @@ class Mailer < ActionMailer::Base
value: options[:value] value: options[:value]
) )
@title = options[:title] && l(options[:title]) @title = options[:title] && l(options[:title])
@originator = options[:originator] || User.current
@remote_ip = options[:remote_ip] || @originator.remote_ip
@url = options[:url] && (options[:url].is_a?(Hash) ? url_for(options[:url]) : options[:url]) @url = options[:url] && (options[:url].is_a?(Hash) ? url_for(options[:url]) : options[:url])
redmine_headers 'Sender' => @originator.login
redmine_headers 'Url' => @url
mail :to => recipients, mail :to => recipients,
:subject => "[#{Setting.app_title}] #{l(:mail_subject_security_notification)}" :subject => "[#{Setting.app_title}] #{l(:mail_subject_security_notification)}"
end end

View file

@ -319,9 +319,10 @@ class Query < ActiveRecord::Base
" INNER JOIN #{table_name_prefix}queries_roles#{table_name_suffix} qr on qr.query_id = q.id" + " INNER JOIN #{table_name_prefix}queries_roles#{table_name_suffix} qr on qr.query_id = q.id" +
" INNER JOIN #{MemberRole.table_name} mr ON mr.role_id = qr.role_id" + " INNER JOIN #{MemberRole.table_name} mr ON mr.role_id = qr.role_id" +
" INNER JOIN #{Member.table_name} m ON m.id = mr.member_id AND m.user_id = ?" + " INNER JOIN #{Member.table_name} m ON m.id = mr.member_id AND m.user_id = ?" +
" INNER JOIN #{Project.table_name} p ON p.id = m.project_id AND p.status <> ?" +
" WHERE q.project_id IS NULL OR q.project_id = m.project_id))" + " WHERE q.project_id IS NULL OR q.project_id = m.project_id))" +
" OR #{table_name}.user_id = ?", " OR #{table_name}.user_id = ?",
VISIBILITY_PUBLIC, VISIBILITY_ROLES, user.id, user.id) VISIBILITY_PUBLIC, VISIBILITY_ROLES, user.id, Project::STATUS_ARCHIVED, user.id)
elsif user.logged? elsif user.logged?
scope.where("#{table_name}.visibility = ? OR #{table_name}.user_id = ?", VISIBILITY_PUBLIC, user.id) scope.where("#{table_name}.visibility = ? OR #{table_name}.user_id = ?", VISIBILITY_PUBLIC, user.id)
else else
@ -340,7 +341,7 @@ class Query < ActiveRecord::Base
if project if project
(user.roles_for_project(project) & roles).any? (user.roles_for_project(project) & roles).any?
else else
Member.where(:user_id => user.id).joins(:roles).where(:member_roles => {:role_id => roles.map(&:id)}).any? user.memberships.joins(:member_roles).where(:member_roles => {:role_id => roles.map(&:id)}).any?
end end
else else
user == self.user user == self.user
@ -398,6 +399,8 @@ class Query < ActiveRecord::Base
params[:v][field] = options[:values] params[:v][field] = options[:values]
end end
params[:c] = column_names params[:c] = column_names
params[:group_by] = group_by.to_s if group_by.present?
params[:t] = totalable_names.map(&:to_s) if totalable_names.any?
params[:sort] = sort_criteria.to_param params[:sort] = sort_criteria.to_param
params[:set_filter] = 1 params[:set_filter] = 1
params params

View file

@ -45,7 +45,7 @@ class Version < ActiveRecord::Base
scope :like, lambda {|arg| scope :like, lambda {|arg|
if arg.present? if arg.present?
pattern = "%#{arg.to_s.strip}%" pattern = "%#{arg.to_s.strip}%"
where("LOWER(#{Version.table_name}.name) LIKE :p", :p => pattern) where([Redmine::Database.like("#{Version.table_name}.name", '?'), pattern])
end end
} }
scope :open, lambda { where(:status => 'open') } scope :open, lambda { where(:status => 'open') }
@ -268,7 +268,7 @@ class Version < ActiveRecord::Base
end end
def deletable? def deletable?
fixed_issues.empty? && !referenced_by_a_custom_field? fixed_issues.empty? && !referenced_by_a_custom_field? && attachments.empty?
end end
def default_project_version def default_project_version

View file

@ -1,7 +1,6 @@
<%= call_hook :view_account_login_top %> <%= call_hook :view_account_login_top %>
<div id="login-form"> <div id="login-form">
<h2><%= l(:label_login) %></h2>
<%= form_tag(signin_path, onsubmit: 'return keepAnchorOnSignIn(this);') do %> <%= form_tag(signin_path, onsubmit: 'return keepAnchorOnSignIn(this);') do %>
<%= back_url_hidden_field_tag %> <%= back_url_hidden_field_tag %>

View file

@ -11,7 +11,7 @@
<% end %> <% end %>
</table> </table>
<br /> <br />
<div class="box"> <div class="box autoscroll">
<pre><%= Redmine::Info.environment %></pre> <pre><%= Redmine::Info.environment %></pre>
</div> </div>

View file

@ -1,6 +1,7 @@
<%= title l(:label_plugins) %> <%= title l(:label_plugins) %>
<% if @plugins.any? %> <% if @plugins.any? %>
<div class="autoscroll">
<table class="list plugins"> <table class="list plugins">
<% @plugins.each do |plugin| %> <% @plugins.each do |plugin| %>
<tr id="plugin-<%= plugin.id %>"> <tr id="plugin-<%= plugin.id %>">
@ -14,6 +15,7 @@
</tr> </tr>
<% end %> <% end %>
</table> </table>
</div>
<p><a href="#" id="check-for-updates"><%= l(:label_check_for_updates) %></a></p> <p><a href="#" id="check-for-updates"><%= l(:label_check_for_updates) %></a></p>
<% else %> <% else %>
<p class="nodata"><%= l(:label_no_data) %></p> <p class="nodata"><%= l(:label_no_data) %></p>
@ -27,7 +29,7 @@ $(document).ready(function(){
dataType: "jsonp", dataType: "jsonp",
url: "https://www.redmine.org/plugins/check_updates", url: "https://www.redmine.org/plugins/check_updates",
data: <%= raw_json plugin_data_for_updates(@plugins) %>, data: <%= raw_json plugin_data_for_updates(@plugins) %>,
timeout: 3000, timeout: 10000,
beforeSend: function(){ beforeSend: function(){
$('#ajax-indicator').show(); $('#ajax-indicator').show();
}, },

View file

@ -39,7 +39,7 @@
<ul> <ul>
<% @priorities.each do |p| -%> <% @priorities.each do |p| -%>
<li><%= context_menu_link p.name, bulk_update_issues_path(:ids => @issue_ids, :issue => {'priority_id' => p}, :back_url => @back), :method => :post, <li><%= context_menu_link p.name, bulk_update_issues_path(:ids => @issue_ids, :issue => {'priority_id' => p}, :back_url => @back), :method => :post,
:selected => (@issue && p == @issue.priority), :disabled => (!@can[:edit]) %></li> :selected => (@issue && p == @issue.priority), :disabled => (!@can[:edit] || @issues.any?(&:priority_derived?)) %></li>
<% end -%> <% end -%>
</ul> </ul>
</li> </li>
@ -97,7 +97,7 @@
<ul> <ul>
<% (0..10).map{|x|x*10}.each do |p| -%> <% (0..10).map{|x|x*10}.each do |p| -%>
<li><%= context_menu_link "#{p}%", bulk_update_issues_path(:ids => @issue_ids, :issue => {'done_ratio' => p}, :back_url => @back), :method => :post, <li><%= context_menu_link "#{p}%", bulk_update_issues_path(:ids => @issue_ids, :issue => {'done_ratio' => p}, :back_url => @back), :method => :post,
:selected => (@issue && p == @issue.done_ratio), :disabled => (!@can[:edit] || @issues.detect {|i| !i.leaf?}) %></li> :selected => (@issue && p == @issue.done_ratio), :disabled => (!@can[:edit] || @issues.any?(&:done_ratio_derived?)) %></li>
<% end -%> <% end -%>
</ul> </ul>
</li> </li>

View file

@ -6,6 +6,7 @@
<% delete_allowed = User.current.allowed_to?(:manage_files, @project) %> <% delete_allowed = User.current.allowed_to?(:manage_files, @project) %>
<div class="autoscroll">
<table class="list files"> <table class="list files">
<thead><tr> <thead><tr>
<%= sort_header_tag('filename', :caption => l(:field_filename)) %> <%= sort_header_tag('filename', :caption => l(:field_filename)) %>
@ -42,5 +43,6 @@
<% end %> <% end %>
</tbody> </tbody>
</table> </table>
</div>
<% html_title(l(:label_attachment_plural)) -%> <% html_title(l(:label_attachment_plural)) -%>

View file

@ -2,6 +2,6 @@ $('#relations').html('<%= escape_javascript(render :partial => 'issues/relations
<% if @relation.errors.empty? %> <% if @relation.errors.empty? %>
$('#relation_delay').val(''); $('#relation_delay').val('');
$('#relation_issue_to_id').val(''); $('#relation_issue_to_id').val('');
$('#relation_issue_to_id').focus();
<% end %> <% end %>
$('#new-relation-form').show(); $('#new-relation-form').show();
$('#relation_issue_to_id').focus();

View file

@ -13,6 +13,8 @@
<% end %> <% end %>
<br /> <br />
<span class="author"><%= authoring(changeset.committed_on, changeset.author) %></span></p> <span class="author"><%= authoring(changeset.committed_on, changeset.author) %></span></p>
<div class="wiki changeset-comments"><%= format_changeset_comments changeset %></div> <div class="wiki changeset-comments">
<%= format_changeset_comments changeset %>
</div>
</div> </div>
<% end %> <% end %>

View file

@ -6,7 +6,9 @@
<p><strong><%= l(:text_destroy_time_entries_question, :hours => number_with_precision(@hours, :precision => 2)) %></strong></p> <p><strong><%= l(:text_destroy_time_entries_question, :hours => number_with_precision(@hours, :precision => 2)) %></strong></p>
<p> <p>
<label><%= radio_button_tag 'todo', 'destroy', true %> <%= l(:text_destroy_time_entries) %></label><br /> <label><%= radio_button_tag 'todo', 'destroy', true %> <%= l(:text_destroy_time_entries) %></label><br />
<% unless Setting.timelog_required_fields.include?('issue_id') %>
<label><%= radio_button_tag 'todo', 'nullify', false %> <%= l(:text_assign_time_entries_to_project) %></label><br /> <label><%= radio_button_tag 'todo', 'nullify', false %> <%= l(:text_assign_time_entries_to_project) %></label><br />
<% end %>
<% if @project %> <% if @project %>
<label><%= radio_button_tag 'todo', 'reassign', false, :onchange => 'if (this.checked) { $("#reassign_to_id").focus(); }' %> <%= l(:text_reassign_time_entries) %></label> <label><%= radio_button_tag 'todo', 'reassign', false, :onchange => 'if (this.checked) { $("#reassign_to_id").focus(); }' %> <%= l(:text_reassign_time_entries) %></label>
<%= text_field_tag 'reassign_to_id', params[:reassign_to_id], :size => 6, :onfocus => '$("#todo_reassign").attr("checked", true);' %> <%= text_field_tag 'reassign_to_id', params[:reassign_to_id], :size => 6, :onfocus => '$("#todo_reassign").attr("checked", true);' %>

View file

@ -4,29 +4,26 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge"/> <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<title><%= html_title %></title> <title><%= html_title %></title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" /> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="description" content="<%= Redmine::Info.app_name %>" /> <meta name="description" content="<%= Redmine::Info.app_name %>" />
<meta name="keywords" content="issue,bug,tracker" /> <meta name="keywords" content="issue,bug,tracker" />
<%= csrf_meta_tag %> <%= csrf_meta_tag %>
<%= favicon %> <%= favicon %>
<%= stylesheet_link_tag 'jquery/jquery-ui-1.11.0', 'cookieconsent.min', 'application', 'responsive', :media => 'all' %> <%= stylesheet_link_tag 'jquery/jquery-ui-1.11.0', 'application', 'responsive', :media => 'all' %>
<%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %> <%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %>
<% is_welcome = !User.current.logged? && current_page?(:controller => 'welcome', :action => 'index') %>
<%= stylesheet_link_tag 'frontpage', :media => 'all' if is_welcome %>
<%= javascript_heads %> <%= javascript_heads %>
<script src="/themes/circlepro/javascripts/cookieconsent.min.js"></script>
<%= heads_for_theme %> <%= heads_for_theme %>
<%= call_hook :view_layouts_base_html_head %> <%= call_hook :view_layouts_base_html_head %>
<!-- page specific tags --> <!-- page specific tags -->
<%= yield :header_tags -%> <%= yield :header_tags -%>
</head> </head>
<body class="<%= body_css_classes %><%= ' is-preload' if is_welcome %>"> <body class="<%= body_css_classes %>">
<%= call_hook :view_layouts_base_body_top %> <%= call_hook :view_layouts_base_body_top %>
<div id="wrapper"> <div id="wrapper">
<div class="flyout-menu js-flyout-menu"> <div class="flyout-menu js-flyout-menu">
<% if User.current.logged? || !Setting.login_required? %> <% if User.current.logged? || !Setting.login_required? %>
<div class="flyout-menu__search"> <div class="flyout-menu__search">
<%= form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get ) do %> <%= form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get ) do %>
@ -64,58 +61,45 @@
<div id="wrapper2"> <div id="wrapper2">
<div id="wrapper3"> <div id="wrapper3">
<div id="top-menu"> <div id="top-menu">
<div id="wrapper-top-menu">
<ul class="social-menu">
<li class="social-link-blog"><a href="https://manuel.cillero.es" title="<%= l(:link_my_blog) %>" class="icon-blog"><span><%= l(:link_my_blog) %></span></a></li>
<li class="social-link-twitter"><a href="https://twitter.com/manuelcillero" title="Twitter" target="_blank" class="icon-twitter"><span>Twitter</span></a></li>
<li class="social-link-facebook"><a href="https://facebook.com/manuelcillero" title="Facebook" target="_blank" class="icon-facebook"><span>Facebook</span></a></li>
<li class="social-link-linkedin"><a href="https://es.linkedin.com/in/manuelcillero" title="Linkedin" target="_blank" class="icon-linkedin"><span>Linkedin</span></a></li>
<li class="social-link-gitlab"><a href="https://gitlab.com/manuelcillero" title="Gitlab" target="_blank" class="icon-gitlab"><span>Gitlab</span></a></li>
<li class="social-link-mail"><a href="https://manuel.cillero.es/contacto/#suitepro" title="Mail" class="icon-mail"><span>Mail</span></a></li>
</ul>
<div id="account"> <div id="account">
<%= render_menu :account_menu -%> <%= render_menu :account_menu -%>
</div> </div>
<%= content_tag('div', "#{l(:label_logged_as)} #{link_to_user(User.current, :format => :username)}".html_safe, :id => 'loggedas') if User.current.logged? %> <%= content_tag('div', "#{l(:label_logged_as)} #{link_to_user(User.current, :format => :username)}".html_safe, :id => 'loggedas') if User.current.logged? %>
<%= render_menu :top_menu if User.current.logged? || !Setting.login_required? -%> <%= render_menu :top_menu if User.current.logged? || !Setting.login_required? -%>
</div> </div>
</div>
<div id="header"> <div id="header">
<a href="#" class="mobile-toggle-button js-flyout-menu-toggle-button"></a> <a href="#" class="mobile-toggle-button js-flyout-menu-toggle-button"></a>
<div id="wrapper-header">
<% if User.current.logged? || !Setting.login_required? %> <% if User.current.logged? || !Setting.login_required? %>
<div id="quick-search" class="hide-when-print"> <div id="quick-search">
<%= form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get ) do %> <%= form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get ) do %>
<%= hidden_field_tag 'scope', default_search_project_scope, :id => nil %> <%= hidden_field_tag 'scope', default_search_project_scope, :id => nil %>
<%= hidden_field_tag(controller.default_search_scope, 1, :id => nil) if controller.default_search_scope %> <%= hidden_field_tag(controller.default_search_scope, 1, :id => nil) if controller.default_search_scope %>
<label for='q'> <label for='q'>
<%= link_to l(:label_search), {:controller => 'search', :action => 'index', :id => @project}, :accesskey => accesskey(:search) %>: <%= link_to l(:label_search), {:controller => 'search', :action => 'index', :id => @project, :scope => default_search_project_scope}, :accesskey => accesskey(:search) %>:
</label> </label>
<%= text_field_tag 'q', @question, :size => 20, :class => 'small', :accesskey => accesskey(:quick_search) %> <%= text_field_tag 'q', @question, :size => 20, :class => 'small', :accesskey => accesskey(:quick_search) %>
<% end %> <% end %>
<%= render_project_jump_box %> <%= render_project_jump_box %>
</div> </div>
<% end %> <% end %>
<h1><%= page_header_title %></h1> <h1><%= page_header_title %></h1>
</div>
<% if display_main_menu?(@project) %> <% if display_main_menu?(@project) %>
<div id="main-menu" class="tabs"> <div id="main-menu" class="tabs">
<div id="wrapper-main-menu">
<%= render_main_menu(@project) %> <%= render_main_menu(@project) %>
<div class="tabs-buttons" style="display:none;"> <div class="tabs-buttons" style="display:none;">
<button class="tab-left" onclick="moveTabLeft(this); return false;"></button> <button class="tab-left" onclick="moveTabLeft(this); return false;"></button>
<button class="tab-right" onclick="moveTabRight(this); return false;"></button> <button class="tab-right" onclick="moveTabRight(this); return false;"></button>
</div> </div>
</div> </div>
</div>
<% end %> <% end %>
</div> </div>
<div id="main" class="<%= sidebar_content? ? '' : 'nosidebar' %>"> <div id="main" class="<%= sidebar_content? ? '' : 'nosidebar' %>">
<div id="wrapper-main">
<div id="sidebar"> <div id="sidebar">
<%= yield :sidebar %> <%= yield :sidebar %>
<%= view_layouts_base_sidebar_hook_response %> <%= view_layouts_base_sidebar_hook_response %>
@ -129,49 +113,17 @@
</div> </div>
</div> </div>
</div> </div>
</div>
<a href="#" id="scrollup" class="hide-when-print"><%=l(:label_sort_higher)%></a><%= javascript_tag "$('#scrollup').click(function(){$('html,body').animate({scrollTop:0},600);return false;});" %>
<div id="ajax-indicator" style="display:none;"><span><%= l(:label_loading) %></span></div> <div id="ajax-indicator" style="display:none;"><span><%= l(:label_loading) %></span></div>
<div id="ajax-modal" style="display:none;"></div> <div id="ajax-modal" style="display:none;"></div>
<div id="footer"> <div id="footer">
<div id="wrapper-footer">
<ul class="social-menu">
<li class="social-link-blog"><a href="https://manuel.cillero.es" title="<%= l(:link_my_blog) %>" class="icon-blog"><span><%= l(:link_my_blog) %></span></a></li>
<li class="social-link-twitter"><a href="https://twitter.com/manuelcillero" title="Twitter" target="_blank" class="icon-twitter"><span>Twitter</span></a></li>
<li class="social-link-facebook"><a href="https://facebook.com/manuelcillero" title="Facebook" target="_blank" class="icon-facebook"><span>Facebook</span></a></li>
<li class="social-link-linkedin"><a href="https://es.linkedin.com/in/manuelcillero" title="Linkedin" target="_blank" class="icon-linkedin"><span>Linkedin</span></a></li>
<li class="social-link-gitlab"><a href="https://gitlab.com/manuelcillero" title="Gitlab" target="_blank" class="icon-gitlab"><span>Gitlab</span></a></li>
<li class="social-link-mail"><a href="https://manuel.cillero.es/contacto/#suitepro" title="Mail" class="icon-mail"><span>Mail</span></a></li>
</ul>
<div class="bgl"><div class="bgr"> <div class="bgl"><div class="bgr">
<div id="legal"> Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %> &copy; 2006-2019 Jean-Philippe Lang
<span class="legal-legal"><a href="/projects/suitepro/wiki/Legal"><%= l(:label_legal) %></a></span>
<span class="legal-terms"> &nbsp;|&nbsp; <a href="/projects/suitepro/wiki/Condiciones_de_uso"><%= l(:label_legal_terms) %></a></span>
<span class="legal-privacy"> &nbsp;|&nbsp; <a href="/projects/suitepro/wiki/Política_de_privacidad"><%= l(:label_legal_privacy) %></a></span>
<span class="legal-cookies"> &nbsp;|&nbsp; <a href="/projects/suitepro/wiki/Política_de_cookies"><%= l(:label_legal_cookies) %></a></span>
</div>
<%= Time.current.year %> &copy; SuitePro (powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %>)
</div></div> </div></div>
</div> </div>
</div> </div>
</div> </div>
</div>
<%= call_hook :view_layouts_base_body_bottom %> <%= call_hook :view_layouts_base_body_bottom %>
<script>
//<![CDATA[
window.addEventListener("load", function(){
window.cookieconsent.initialise({
"palette": { "popup": { "background": "rgba(20,20,20,0.8)" }, "button": { "background": "#fff" } },
"theme": "classic",
"position": "bottom-left",
"content": { "message": "<a href='https://suitepro.cillero.es'>SuitePro</a> requiere el uso de cookies para ofrecer la mejor experiencia de acceso a sus contenidos. Puedes aceptar su uso o abandonar la página si lo deseas.", "dismiss": "ACEPTO SU USO", "link": "Más información", "href": "/projects/suitepro/wiki/Pol%C3%ADtica_de_cookies", "target": "_self" }
})});
//]]>
</script>
</body> </body>
</html> </html>

View file

@ -7,7 +7,7 @@
<%= content_tag :h1, @title -%> <%= content_tag :h1, @title -%>
<% end %></p> <% end %></p>
<p><%= l(:field_user) %>: <strong><%= User.current.login %></strong><br/> <p><%= l(:field_user) %>: <strong><%= @originator.login %></strong><br/>
<%= l(:field_remote_ip) %>: <strong><%= User.current.remote_ip %></strong><br/> <%= l(:field_remote_ip) %>: <strong><%= @remote_ip %></strong><br/>
<%= l(:label_date) %>: <strong><%= format_time Time.now, true, @user %></strong></p> <%= l(:label_date) %>: <strong><%= format_time Time.now, true, @user %></strong></p>

View file

@ -2,7 +2,7 @@
<%= @url || @title %> <%= @url || @title %>
<%= l(:field_user) %>: <%= User.current.login %> <%= l(:field_user) %>: <%= @originator.login %>
<%= l(:field_remote_ip) %>: <%= User.current.remote_ip %> <%= l(:field_remote_ip) %>: <%= @remote_ip %>
<%= l(:label_date) %>: <%= format_time Time.now, true, @user %> <%= l(:label_date) %>: <%= format_time Time.now, true, @user %>

View file

@ -7,7 +7,7 @@
</div> </div>
</fieldset> </fieldset>
<fieldset class="box"> <fieldset class="box">
<legend><%= l(:label_role_plural) %> <%= toggle_checkboxes_link('.roles-selection input') %></legend> <legend><%= toggle_checkboxes_link('.roles-selection input') %><%= l(:label_role_plural) %></legend>
<div class="roles-selection"> <div class="roles-selection">
<% User.current.managed_roles(@project).each do |role| %> <% User.current.managed_roles(@project).each do |role| %>
<label><%= check_box_tag 'membership[role_ids][]', role.id, false, :id => nil %> <%= role %></label> <label><%= check_box_tag 'membership[role_ids][]', role.id, false, :id => nil %> <%= role %></label>

View file

@ -1,5 +1,5 @@
<fieldset class="box"> <fieldset class="box">
<legend><%= l(:label_project_plural) %> <%= toggle_checkboxes_link('.projects-selection input:enabled') %></legend> <legend><%= toggle_checkboxes_link('.projects-selection input:enabled') %><%= l(:label_project_plural) %></legend>
<div class="objects-selection"> <div class="objects-selection">
<div class="projects-selection"> <div class="projects-selection">
<%= render_project_nested_lists(@projects) do |p| %> <%= render_project_nested_lists(@projects) do |p| %>
@ -12,7 +12,7 @@
</fieldset> </fieldset>
<fieldset class="box"> <fieldset class="box">
<legend><%= l(:label_role_plural) %> <%= toggle_checkboxes_link('.roles-selection input') %></legend> <legend><%= toggle_checkboxes_link('.roles-selection input') %><%= l(:label_role_plural) %></legend>
<div class="roles-selection"> <div class="roles-selection">
<% @roles.each do |role| %> <% @roles.each do |role| %>
<label> <label>

View file

@ -3,6 +3,7 @@
<% end %> <% end %>
<% if @project.repositories.any? %> <% if @project.repositories.any? %>
<div class="autoscroll">
<table class="list"> <table class="list">
<thead> <thead>
<tr> <tr>
@ -36,6 +37,7 @@
<% end %> <% end %>
</tbody> </tbody>
</table> </table>
</div>
<% else %> <% else %>
<p class="nodata"><%= l(:label_no_data) %></p> <p class="nodata"><%= l(:label_no_data) %></p>
<% end %> <% end %>

View file

@ -33,7 +33,9 @@
</div> </div>
<div class="wiki changeset-comments"><%= format_changeset_comments @changeset %></div> <div class="wiki changeset-comments">
<%= format_changeset_comments @changeset %>
</div>
<% if @changeset.issues.visible.any? || User.current.allowed_to?(:manage_related_issues, @repository.project) %> <% if @changeset.issues.visible.any? || User.current.allowed_to?(:manage_related_issues, @repository.project) %>
<%= render :partial => 'related_issues' %> <%= render :partial => 'related_issues' %>

View file

@ -21,10 +21,11 @@
</p> </p>
<%= hidden_field_tag 'tracker[core_fields][]', '' %> <%= hidden_field_tag 'tracker[core_fields][]', '' %>
<% if IssueCustomField.all.any? %> <% @issue_custom_fields = IssueCustomField.sorted %>
<% if @issue_custom_fields.present? %>
<p> <p>
<label><%= l(:label_custom_field_plural) %></label> <label><%= l(:label_custom_field_plural) %></label>
<% IssueCustomField.all.each do |field| %> <% @issue_custom_fields.each do |field| %>
<label class="block"> <label class="block">
<%= check_box_tag 'tracker[custom_field_ids][]',field.id, @tracker.custom_fields.to_a.include?(field), :id => nil %> <%= check_box_tag 'tracker[custom_field_ids][]',field.id, @tracker.custom_fields.to_a.include?(field), :id => nil %>
<%= field.name %> <%= field.name %>

View file

@ -1,5 +1,3 @@
<% if User.current.logged? %>
<h2><%= l(:label_home) %></h2> <h2><%= l(:label_home) %></h2>
<div class="splitcontentleft"> <div class="splitcontentleft">
@ -26,149 +24,3 @@
<%= auto_discovery_link_tag(:atom, {:controller => 'activities', :action => 'index', :key => User.current.rss_key, :format => 'atom'}, <%= auto_discovery_link_tag(:atom, {:controller => 'activities', :action => 'index', :key => User.current.rss_key, :format => 'atom'},
:title => "#{Setting.app_title}: #{l(:label_activity)}") %> :title => "#{Setting.app_title}: #{l(:label_activity)}") %>
<% end %> <% end %>
<% else %><!-- Frontpage -->
<div id="fp"><%= text_field_tag 'forcetop', nil, :style => 'display: none;' %>
<!-- Banner -->
<section id="fp-banner">
<div class="inner">
<h2>SuitePro</h2>
<p><%= l(:welcome_suitepro) %></p>
<ul class="actions special">
<li><a href="#fp-login" class="button primary scrolly"><%= l(:label_login) %></a></li>
</ul>
</div>
<a href="#one" class="more scrolly"><%= l(:welcome_discover) %></a>
</section>
<!-- One -->
<section id="one" class="frapper style1 special">
<div class="inner">
<header class="major">
<h2>A SIMPLE WAY TO GET WORK DONE&nbsp;&nbsp;;)</h2>
<p><%= raw l(:welcome_suitepro_is_redmine, :suitepro => '<strong>SuitePro</strong>', :redmine => '<a href="https://www.redmine.org"><strong>Redmine</strong></a>') %></p>
</header>
<ul class="icons major">
<li><span class="fp-icon fp-icon_1 major style1"><span class="label">Lorem</span></span></li>
<li><span class="fp-icon fp-icon_2 major style2"><span class="label">Ipsum</span></span></li>
<li><span class="fp-icon fp-icon_3 major style3"><span class="label">Dolor</span></span></li>
</ul>
</div>
</section>
<!-- Two -->
<section id="two" class="frapper alt style2">
<section class="spotlight">
<div class="image"><%= image_tag '/themes/circlepro/images/pic01.jpg' %></div><div class="content">
<h2><%= raw l(:welcome_spotlight_1_title) %></h2>
<p><%= l(:welcome_spotlight_1_text) %></p>
</div>
</section>
<section class="spotlight">
<div class="image"><%= image_tag '/themes/circlepro/images/pic02.jpg' %></div><div class="content">
<h2><%= raw l(:welcome_spotlight_2_title) %></h2>
<p><%= l(:welcome_spotlight_2_text) %></p>
</div>
</section>
<section class="spotlight">
<div class="image"><%= image_tag '/themes/circlepro/images/pic03.jpg' %></div><div class="content">
<h2><%= raw l(:welcome_spotlight_3_title) %></h2>
<p><%= l(:welcome_spotlight_3_text) %></p>
</div>
</section>
</section>
<!-- Three -->
<section id="three" class="frapper style3 special">
<div class="inner">
<header class="major">
<h2><%= l(:welcome_other_features) %></h2>
</header>
<ul class="features">
<li class="fp-icon fp-icon_4">
<h3><%= l(:welcome_feature_1_title) %></h3>
<p><%= l(:welcome_feature_1_text) %></p>
</li>
<li class="fp-icon fp-icon_5">
<h3><%= l(:welcome_feature_2_title) %></h3>
<p><%= l(:welcome_feature_2_text) %></p>
</li>
<li class="fp-icon fp-icon_6">
<h3><%= l(:welcome_feature_3_title) %></h3>
<p><%= l(:welcome_feature_3_text) %></p>
</li>
<li class="fp-icon fp-icon_7">
<h3><%= l(:welcome_feature_4_title) %></h3>
<p><%= l(:welcome_feature_4_text) %></p>
</li>
<li class="fp-icon fp-icon_8">
<h3><%= l(:welcome_feature_5_title) %></h3>
<p><%= l(:welcome_feature_5_text) %></p>
</li>
<li class="fp-icon fp-icon_9">
<h3><%= l(:welcome_feature_6_title) %></h3>
<p><%= l(:welcome_feature_6_text) %></p>
</li>
</ul>
</div>
</section>
<!-- Login -->
<section id="fp-login" class="frapper style4">
<div class="inner">
<%= call_hook :view_account_login_top %>
<div id="login-form">
<h2><%= l(:label_login) %></h2>
<%= form_tag(signin_path, onsubmit: 'return keepAnchorOnSignIn(this);') do %>
<%= back_url_hidden_field_tag %>
<label for="username"><%=l(:field_login)%></label>
<%= text_field_tag 'username', params[:username], :tabindex => '1' %>
<label for="password">
<%=l(:field_password)%>
<%= link_to l(:label_password_lost), lost_password_path, :class => "lost_password" if Setting.lost_password? %>
</label>
<%= password_field_tag 'password', nil, :tabindex => '2' %>
<% if Setting.openid? %>
<label for="openid_url"><%=l(:field_identity_url)%></label>
<%= text_field_tag "openid_url", nil, :tabindex => '3' %>
<% end %>
<% if Setting.autologin? %>
<label for="autologin"><%= check_box_tag 'autologin', 1, false, :tabindex => 4 %> <%= l(:label_stay_logged_in) %></label>
<% end %>
<input type="submit" name="login" value="<%=l(:button_login)%>" tabindex="5" id="login-submit" />
<% end %>
</div>
</div>
</section>
<!-- CTA -->
<section id="fp-cta" class="frapper style4">
<div class="inner">
<header>
<h2><%= l(:welcome_any_questions) %></h2>
<p><%= l(:welcome_please_contact) %></p>
</header>
<ul class="actions stacked">
<li><a href="https://manuel.cillero.es/contacto/#suitepro" class="button fit primary"><%= l(:welcome_contact) %></a></li>
<li><a href="https://manuel.cillero.es/sobre-mi" class="button fit"><%= l(:welcome_about_me) %></a></li>
</ul>
</div>
</section>
</div>
<script src="/themes/circlepro/javascripts/frontpage/jquery.scrolly.min.js"></script>
<script src="/themes/circlepro/javascripts/frontpage/browser.min.js"></script>
<script src="/themes/circlepro/javascripts/frontpage/breakpoints.min.js"></script>
<script src="/themes/circlepro/javascripts/frontpage/util.js"></script>
<script src="/themes/circlepro/javascripts/frontpage/main.js"></script>
<% end %>

View file

@ -46,8 +46,7 @@
<%= render(:partial => "wiki/content", :locals => {:content => @content}) %> <%= render(:partial => "wiki/content", :locals => {:content => @content}) %>
<% if @page.attachments.length > 0 || (@editable && authorize_for('wiki', 'add_attachment')) %> <fieldset class="collapsible collapsed hide-when-print">
<fieldset class="collapsible collapsed<% if @page.attachments.length == 0 %> hide-when-print<% end %>">
<legend onclick="toggleFieldset(this);"><%= l(:label_attachment_plural) %> (<%= @page.attachments.length %>)</legend> <legend onclick="toggleFieldset(this);"><%= l(:label_attachment_plural) %> (<%= @page.attachments.length %>)</legend>
<div style="display: none;"> <div style="display: none;">
@ -67,7 +66,6 @@
<% end %> <% end %>
</div> </div>
</fieldset> </fieldset>
<% end %>
<% other_formats_links do |f| %> <% other_formats_links do |f| %>
<%= f.link_to 'PDF', :url => {:id => @page.title, :version => params[:version]} %> <%= f.link_to 'PDF', :url => {:id => @page.title, :version => params[:version]} %>

View file

@ -1211,8 +1211,8 @@ de:
label_table_of_contents: Inhaltsverzeichnis label_table_of_contents: Inhaltsverzeichnis
error_no_projects_with_tracker_allowed_for_new_issue: Es gibt keine Projekte mit Trackern, für welche sie Tickets erzeugen können error_no_projects_with_tracker_allowed_for_new_issue: Es gibt keine Projekte mit Trackern, für welche sie Tickets erzeugen können
field_textarea_font: Schriftart für Textbereiche field_textarea_font: Schriftart für Textbereiche
label_font_default: Strandardschrift label_font_default: Standardschrift
label_font_monospace: Nichtproporzionale Schrift label_font_monospace: Nichtproportionale Schrift
label_font_proportional: Proportionale Schrift label_font_proportional: Proportionale Schrift
setting_commit_logs_formatting: Textformatierung für Commit Nachrichten setting_commit_logs_formatting: Textformatierung für Commit Nachrichten
setting_mail_handler_enable_regex_delimiters: Reguläre Ausdrücke erlauben setting_mail_handler_enable_regex_delimiters: Reguläre Ausdrücke erlauben

View file

@ -1209,40 +1209,3 @@ en:
description_issue_category_reassign: Choose issue category description_issue_category_reassign: Choose issue category
description_wiki_subpages_reassign: Choose new parent page description_wiki_subpages_reassign: Choose new parent page
text_repository_identifier_info: 'Only lower case letters (a-z), numbers, dashes and underscores are allowed.<br />Once saved, the identifier cannot be changed.' text_repository_identifier_info: 'Only lower case letters (a-z), numbers, dashes and underscores are allowed.<br />Once saved, the identifier cannot be changed.'
welcome_suitepro: Planning, knowledge sharing, clients support and personal productivity, with both powerful and simplicity in mind.
welcome_discover: Discover
welcome_suitepro_is_redmine: "%{suitepro} is powered by %{redmine}, the popular project management and issue tracking solution based on the Ruby on Rails framework. Redmine lets us have a powerful workflow for getting tasks done and keep information in one place."
welcome_spotlight_1_title: "The Basics: Projects,<br /> Tasks, Issue Tracking"
welcome_spotlight_1_text: Keep track of everything, with visual indicators to monitorize tasks and subtasks in order to stay up to date with milestones, track of time, workflows and all that requires attention.
welcome_spotlight_2_title: "Documents, Wikis,<br /> File Management"
welcome_spotlight_2_text: Keep documents and files availables wherever you are. Use the wiki project to save project requeriments, attached files, install and user guides, or meeting minutes at your fingertips.
welcome_spotlight_3_title: "Flexible control<br /> of user access"
welcome_spotlight_3_text: Using a role-based approach, roles are a collection of permissions outlining how users can operate with the projects. Each member of a project can have one or more roles assigned by administrators.
welcome_other_features: Other Features
welcome_feature_1_title: Gantt Chart And Calendar
welcome_feature_1_text: The gantt chart displays issues that have a start date and a due date. The calendar provides an overview of a project as a monthly view.
welcome_feature_2_title: News And Forums
welcome_feature_2_text: News to show information about the status of projects or any other subjects. The forums allow users from a project to communicate with each others.
welcome_feature_3_title: Email notifications And Feeds
welcome_feature_3_text: SuitePro can be configured to receive notifications via email. It also provides web feeds to use with external readers or aggregators.
welcome_feature_4_title: Code Repositories
welcome_feature_4_text: Version Control Systems like Git or Subversion can be used as code repositories and also keep track of changes made to the code.
welcome_feature_5_title: Responsive Design
welcome_feature_5_text: SuitePro is optimized to look great on mobile devices thanks to its responsive design. All pages adapt automatically to the screen size on a mobile phone, tablet or desktop computer.
welcome_feature_6_title: Is Open Source Software
welcome_feature_6_text: It means you are not locked into using a particular vendors system, and its continually evolving in real time as developers add to it and modify it.
welcome_any_questions: Any Questions?
welcome_please_contact: Please feel free to contact me if you need any further information.
welcome_contact: Contact
welcome_about_me: More About Me
link_my_blog: My Blog
label_legal: Legal notice
label_legal_terms: Terms of use
label_legal_privacy: Privacy policy
label_legal_cookies: Cookies policy

View file

@ -504,7 +504,7 @@ es:
label_loading: Cargando... label_loading: Cargando...
label_logged_as: Conectado como label_logged_as: Conectado como
label_login: Iniciar sesión label_login: Iniciar sesión
label_logout: Cerrar sesión label_logout: Terminar sesión
label_max_size: Tamaño máximo label_max_size: Tamaño máximo
label_me: yo mismo label_me: yo mismo
label_member: Miembro label_member: Miembro
@ -543,7 +543,7 @@ es:
label_optional_description: Descripción opcional label_optional_description: Descripción opcional
label_options: Opciones label_options: Opciones
label_overall_activity: Actividad global label_overall_activity: Actividad global
label_overview: Resumen label_overview: Vistazo
label_password_lost: ¿Olvidaste la contraseña? label_password_lost: ¿Olvidaste la contraseña?
label_permissions: Permisos label_permissions: Permisos
label_permissions_report: Informe de permisos label_permissions_report: Informe de permisos
@ -1217,7 +1217,7 @@ es:
mail_body_security_notification_notify_disabled: Se han desactivado las notificaciones para el correo electrónico %{value} mail_body_security_notification_notify_disabled: Se han desactivado las notificaciones para el correo electrónico %{value}
mail_body_settings_updated: ! 'Las siguientes opciones han sido actualizadas:' mail_body_settings_updated: ! 'Las siguientes opciones han sido actualizadas:'
field_remote_ip: Dirección IP field_remote_ip: Dirección IP
label_wiki_page_new: Nueva página label_wiki_page_new: Nueva pagina wiki
label_relations: Relaciones label_relations: Relaciones
button_filter: Filtro button_filter: Filtro
mail_body_password_updated: Su contraseña se ha cambiado. mail_body_password_updated: Su contraseña se ha cambiado.
@ -1233,7 +1233,7 @@ es:
label_font_monospace: Fuente Monospaced label_font_monospace: Fuente Monospaced
label_font_proportional: Fuente Proportional label_font_proportional: Fuente Proportional
setting_timespan_format: Time span format setting_timespan_format: Time span format
label_table_of_contents: Tabla de contenidos label_table_of_contents: Table of contents
setting_commit_logs_formatting: Apply text formatting to commit messages setting_commit_logs_formatting: Apply text formatting to commit messages
setting_mail_handler_enable_regex_delimiters: Enable regular expressions setting_mail_handler_enable_regex_delimiters: Enable regular expressions
error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new
@ -1256,41 +1256,3 @@ es:
permission_view_news: View news permission_view_news: View news
label_no_preview_alternative_html: No preview available. %{link} the file instead. label_no_preview_alternative_html: No preview available. %{link} the file instead.
label_no_preview_download: Download label_no_preview_download: Download
welcome_suitepro: Potencia y sencillez para planificar, compartir conocimiento, prestar soporte a clientes y acelerar la productividad.
welcome_discover: Descubre
welcome_suitepro_is_redmine: "%{suitepro} es %{redmine}, la conocida herramienta para la gestión de proyectos y el seguimiento de peticiones basada en Ruby on Rails. Redmine apremia la finalización de las tareas y mantiene la información en un único sitio."
welcome_spotlight_1_title: "Lo Básico: Proyectos,<br /> Tareas, Peticiones"
welcome_spotlight_1_text: Podrás hacer un seguimiento completo de todo, monitorizar tareas y subtareas para estar al día de los hitos de proyecto, controlar los tiempos, los flujos de trabajo o cualquier elemento que requiera atención.
welcome_spotlight_2_title: "Documentos, Wikis,<br /> Gestión de Archivos"
welcome_spotlight_2_text: Organizados para disponer de los documentos y los archivos allá donde se esté. Y el wiki de proyecto para estructurar los requerimientos, las guías de instalación y de usuario, o las actas de trabajo.
welcome_spotlight_3_title: "Control de accesos<br /> flexible"
welcome_spotlight_3_text: Usando permisos agrupados en roles para establecer cómo pueden operar los usuarios en los proyectos. Cada miembro de un proyecto podrá tener uno o más roles asignados por los administradores.
welcome_other_features: Otras Características
welcome_feature_1_title: Diagrama de Gantt y Calendario
welcome_feature_1_text: El diagrama de Gantt muestra las tareas que tienen fecha de inicio y vencimiento. Y el calendario da una visión general de los proyectos en una vista mensual.
welcome_feature_2_title: Noticias y Foros
welcome_feature_2_text: Las noticias muestran información sobre novedades en los proyectos u otros temas de interés. Y los foros permiten que los usuarios de un proyecto se comuniquen entre sí.
welcome_feature_3_title: Notificaciones y Sindicación
welcome_feature_3_text: SuitePro se puede configurar para recibir notificaciones por correo electrónico. Y también proporciona sindicaciones para utilizar con agregadores externos.
welcome_feature_4_title: Repositorios de Código
welcome_feature_4_text: Sistemas de Control de Versiones como Git o Subversion pueden usarse como repositorios de código y seguir desde SuitePro los cambios realizados en el código.
welcome_feature_5_title: Responsive Design
welcome_feature_5_text: SuitePro está optimizado para visualizarse en dispositivos móviles. Las páginas se adaptan automáticamente al tamaño de la pantalla, ya sea un teléfono móvil, una tableta o un ordenador.
welcome_feature_6_title: Es Software de Código Abierto
welcome_feature_6_text: Esto supone no estar limitado por ningún proveedor, y seguir en permanente evolución con desarrolladores que se involucran continuamente.
welcome_any_questions: ¿Alguna Pregunta?
welcome_please_contact: No dudar en contactar conmigo para obtener más información.
welcome_contact: Contactar
welcome_about_me: Más Sobre Mí
link_my_blog: Mi blog personal
label_legal: Aviso legal
label_legal_terms: Condiciones de uso
label_legal_privacy: Política de privacidad
label_legal_cookies: Uso de cookies

View file

@ -140,8 +140,8 @@ gl:
invalid: "non é válido" invalid: "non é válido"
confirmation: "non coincide coa confirmación" confirmation: "non coincide coa confirmación"
accepted: "debe ser aceptado" accepted: "debe ser aceptado"
empty: "non pode estar valeiro" empty: "non pode estar baleiro"
blank: "non pode estar en blanco" blank: "non pode estar en branco"
too_long: "é demasiado longo (non máis de %{count} carácteres)" too_long: "é demasiado longo (non máis de %{count} carácteres)"
too_short: "é demasiado curto (non menos de %{count} carácteres)" too_short: "é demasiado curto (non menos de %{count} carácteres)"
wrong_length: "non ten a lonxitude correcta (debe ser de %{count} carácteres)" wrong_length: "non ten a lonxitude correcta (debe ser de %{count} carácteres)"
@ -426,7 +426,7 @@ gl:
label_f_hour: "%{value} hora" label_f_hour: "%{value} hora"
label_f_hour_plural: "%{value} horas" label_f_hour_plural: "%{value} horas"
label_feed_plural: Feeds label_feed_plural: Feeds
label_feeds_access_key_created_on: "Clave de acceso por Atom creada hai %{value}" label_feeds_access_key_created_on: "Chave de acceso por Atom creada hai %{value}"
label_file_added: Ficheiro engadido label_file_added: Ficheiro engadido
label_file_plural: Ficheiros label_file_plural: Ficheiros
label_filter_add: Engadir o filtro label_filter_add: Engadir o filtro
@ -435,7 +435,7 @@ gl:
label_follows: posterior a label_follows: posterior a
label_gantt: Gantt label_gantt: Gantt
label_general: Xeral label_general: Xeral
label_generate_key: Xerar clave label_generate_key: Xerar chave
label_help: Axuda label_help: Axuda
label_history: Histórico label_history: Histórico
label_home: Inicio label_home: Inicio
@ -648,7 +648,7 @@ gl:
notice_email_error: "Ocorreu un error enviando o correo (%{value})" notice_email_error: "Ocorreu un error enviando o correo (%{value})"
notice_email_sent: "Enviouse un correo a %{value}" notice_email_sent: "Enviouse un correo a %{value}"
notice_failed_to_save_issues: "Imposible gravar %{count} petición(s) de %{total} seleccionada(s): %{ids}." notice_failed_to_save_issues: "Imposible gravar %{count} petición(s) de %{total} seleccionada(s): %{ids}."
notice_feeds_access_key_reseted: A súa clave de acceso para Atom reiniciouse. notice_feeds_access_key_reseted: A súa chave de acceso para Atom reiniciouse.
notice_file_not_found: A páxina á que tenta acceder non existe. notice_file_not_found: A páxina á que tenta acceder non existe.
notice_locking_conflict: Os datos modificáronse por outro usuario. notice_locking_conflict: Os datos modificáronse por outro usuario.
notice_no_issue_selected: "Ningunha petición seleccionada. Por favor, comprobe a petición que quere modificar" notice_no_issue_selected: "Ningunha petición seleccionada. Por favor, comprobe a petición que quere modificar"
@ -720,8 +720,8 @@ gl:
setting_autofetch_changesets: Autorechear as remisións do repositorio setting_autofetch_changesets: Autorechear as remisións do repositorio
setting_autologin: "Identificarse automaticamente." setting_autologin: "Identificarse automaticamente."
setting_bcc_recipients: Ocultar as copias de carbón (bcc) setting_bcc_recipients: Ocultar as copias de carbón (bcc)
setting_commit_fix_keywords: Palabras clave para a corrección setting_commit_fix_keywords: Palabras chave para a corrección
setting_commit_ref_keywords: Palabras clave para a referencia setting_commit_ref_keywords: Palabras chave para a referencia
setting_cross_project_issue_relations: Permitir relacionar peticións de distintos proxectos setting_cross_project_issue_relations: Permitir relacionar peticións de distintos proxectos
setting_date_format: Formato da data setting_date_format: Formato da data
setting_default_language: Idioma predeterminado setting_default_language: Idioma predeterminado
@ -738,7 +738,7 @@ gl:
setting_login_required: Requírese identificación setting_login_required: Requírese identificación
setting_mail_from: Correo dende o que enviar mensaxes setting_mail_from: Correo dende o que enviar mensaxes
setting_mail_handler_api_enabled: Activar o programa para mensaxes entrantes setting_mail_handler_api_enabled: Activar o programa para mensaxes entrantes
setting_mail_handler_api_key: Clave da API setting_mail_handler_api_key: Chave da API
setting_per_page_options: Obxectos por páxina setting_per_page_options: Obxectos por páxina
setting_plain_text_mail: só texto plano (non HTML) setting_plain_text_mail: só texto plano (non HTML)
setting_protocol: Protocolo setting_protocol: Protocolo
@ -1121,118 +1121,118 @@ gl:
setting_link_copied_issue: "Ligar aos tíckets ao copialos" setting_link_copied_issue: "Ligar aos tíckets ao copialos"
label_link_copied_issue: "Ligar ao tícket copiado" label_link_copied_issue: "Ligar ao tícket copiado"
label_ask: "Preguntar" label_ask: "Preguntar"
label_search_attachments_yes: Search attachment filenames and descriptions label_search_attachments_yes: Buscar nomes e descricións dos ficheiros
label_search_attachments_no: Do not search attachments label_search_attachments_no: Non buscar ficheiros
label_search_attachments_only: Search attachments only label_search_attachments_only: Buscar só ficheiros
label_search_open_issues_only: Open issues only label_search_open_issues_only: Só peticións abertas
field_address: Correo electrónico field_address: Correo electrónico
setting_max_additional_emails: Maximum number of additional email addresses setting_max_additional_emails: Máximo número de enderezos de correo adicionais
label_email_address_plural: Emails label_email_address_plural: Correos
label_email_address_add: Add email address label_email_address_add: Engadir enderezo de correo
label_enable_notifications: Enable notifications label_enable_notifications: Activar notificacións
label_disable_notifications: Disable notifications label_disable_notifications: Desactivar notificacións
setting_search_results_per_page: Search results per page setting_search_results_per_page: Resultados da busca por páxina
label_blank_value: blank label_blank_value: En branco
permission_copy_issues: Copy issues permission_copy_issues: Copiar peticións
error_password_expired: Your password has expired or the administrator requires you error_password_expired: A túa contrasinal caducou ou o administrador obrígate
to change it. a cambiala.
field_time_entries_visibility: Time logs visibility field_time_entries_visibility: Visibilidade das entradas de tempo
setting_password_max_age: Require password change after setting_password_max_age: Obrigar a cambiar a contrasinal despois de
label_parent_task_attributes: Parent tasks attributes label_parent_task_attributes: Atributos da tarefa pai
label_parent_task_attributes_derived: Calculated from subtasks label_parent_task_attributes_derived: Calculada a partir das subtarefas
label_parent_task_attributes_independent: Independent of subtasks label_parent_task_attributes_independent: Independente das subtarefas
label_time_entries_visibility_all: All time entries label_time_entries_visibility_all: Todas as entradas de tempo
label_time_entries_visibility_own: Time entries created by the user label_time_entries_visibility_own: Horas creadas polo usuario
label_member_management: Member management label_member_management: Xestión de membros
label_member_management_all_roles: All roles label_member_management_all_roles: Todos os roles
label_member_management_selected_roles_only: Only these roles label_member_management_selected_roles_only: Só estes roles
label_password_required: Confirm your password to continue label_password_required: Confirma a túa contrasinal para continuar
label_total_spent_time: "Tempo total empregado" label_total_spent_time: "Tempo total empregado"
notice_import_finished: "%{count} items have been imported" notice_import_finished: "%{count} elementos foron importados"
notice_import_finished_with_errors: "%{count} out of %{total} items could not be imported" notice_import_finished_with_errors: "%{count} dun total de %{total} elementos non puideron ser importados"
error_invalid_file_encoding: The file is not a valid %{encoding} encoded file error_invalid_file_encoding: O ficheiro non é un ficheiro codificado %{encoding} válido
error_invalid_csv_file_or_settings: The file is not a CSV file or does not match the error_invalid_csv_file_or_settings: O ficheiro non é un arquivo CSV ou non coincide coas
settings below opcións de abaixo
error_can_not_read_import_file: An error occurred while reading the file to import error_can_not_read_import_file: Aconteceu un erro lendo o ficheiro a importar
permission_import_issues: Import issues permission_import_issues: Importar peticións
label_import_issues: Import issues label_import_issues: Importar peticións
label_select_file_to_import: Select the file to import label_select_file_to_import: Selecciona o ficheiro a importar
label_fields_separator: Field separator label_fields_separator: Separador dos campos
label_fields_wrapper: Field wrapper label_fields_wrapper: Envoltorio dos campos
label_encoding: Encoding label_encoding: Codificación
label_comma_char: Comma label_comma_char: Coma
label_semi_colon_char: Semicolon label_semi_colon_char: Punto e coma
label_quote_char: Quote label_quote_char: Comilla simple
label_double_quote_char: Double quote label_double_quote_char: Comilla dobre
label_fields_mapping: Fields mapping label_fields_mapping: Mapeo de campos
label_file_content_preview: File content preview label_file_content_preview: Vista previa do contido
label_create_missing_values: Create missing values label_create_missing_values: Crear valores non presentes
button_import: Import button_import: Importar
field_total_estimated_hours: Total estimated time field_total_estimated_hours: Total de tempo estimado
label_api: API label_api: API
label_total_plural: Totals label_total_plural: Totais
label_assigned_issues: Assigned issues label_assigned_issues: Peticións asignadas
label_field_format_enumeration: Key/value list label_field_format_enumeration: Listaxe chave/valor
label_f_hour_short: '%{value} h' label_f_hour_short: '%{value} h'
field_default_version: Default version field_default_version: Versión predeterminada
error_attachment_extension_not_allowed: Attachment extension %{extension} is not allowed error_attachment_extension_not_allowed: A extensión anexada %{extension} non é permitida
setting_attachment_extensions_allowed: Allowed extensions setting_attachment_extensions_allowed: Extensións permitidas
setting_attachment_extensions_denied: Disallowed extensions setting_attachment_extensions_denied: Extensións prohibidas
label_any_open_issues: any open issues label_any_open_issues: Calquera petición aberta
label_no_open_issues: no open issues label_no_open_issues: Peticións non abertas
label_default_values_for_new_users: Default values for new users label_default_values_for_new_users: Valor predeterminado para novos usuarios
error_ldap_bind_credentials: Invalid LDAP Account/Password error_ldap_bind_credentials: A conta/contrasinal do LDAP non é válida
setting_sys_api_key: Clave da API setting_sys_api_key: Chave da API
setting_lost_password: "Esqueceu o contrasinal?" setting_lost_password: Esqueceu a contrasinal?
mail_subject_security_notification: Security notification mail_subject_security_notification: Notificación de seguridade
mail_body_security_notification_change: ! '%{field} was changed.' mail_body_security_notification_change: ! '%{field} modificado.'
mail_body_security_notification_change_to: ! '%{field} was changed to %{value}.' mail_body_security_notification_change_to: ! '%{field} modificado por %{value}.'
mail_body_security_notification_add: ! '%{field} %{value} was added.' mail_body_security_notification_add: ! '%{field} %{value} engadido.'
mail_body_security_notification_remove: ! '%{field} %{value} was removed.' mail_body_security_notification_remove: ! '%{field} %{value} eliminado.'
mail_body_security_notification_notify_enabled: Email address %{value} now receives mail_body_security_notification_notify_enabled: O correo electrónico %{value} agora recibirá
notifications. notificacións
mail_body_security_notification_notify_disabled: Email address %{value} no longer mail_body_security_notification_notify_disabled: O correo electrónico %{value} deixará de recibir
receives notifications. notificacións
mail_body_settings_updated: ! 'The following settings were changed:' mail_body_settings_updated: ! 'As seguintes opcións foron actualizadas:'
field_remote_ip: IP address field_remote_ip: Enderezo IP
label_wiki_page_new: New wiki page label_wiki_page_new: Nova páxina wiki
label_relations: Relations label_relations: Relacións
button_filter: Filter button_filter: Filtro
mail_body_password_updated: Your password has been changed. mail_body_password_updated: A súa contrasinal foi cambiada.
label_no_preview: No preview available label_no_preview: Non hai vista previa dispoñible
error_no_tracker_allowed_for_new_issue_in_project: The project doesn't have any trackers error_no_tracker_allowed_for_new_issue_in_project: O proxecto non ten ningún tipo
for which you can create an issue para que poida crear unha petición
label_tracker_all: All trackers label_tracker_all: Tódolos tipos
label_new_project_issue_tab_enabled: Display the "New issue" tab label_new_project_issue_tab_enabled: Amosar a lapela "Nova petición"
setting_new_item_menu_tab: Project menu tab for creating new objects setting_new_item_menu_tab: Lapela no menú de cada proxecto para creación de novos obxectos
label_new_object_tab_enabled: Display the "+" drop-down label_new_object_tab_enabled: Amosar a lista despregable "+"
error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers error_no_projects_with_tracker_allowed_for_new_issue: Non hai proxectos con tipos
for which you can create an issue para as que poida crear unha petición
field_textarea_font: Font used for text areas field_textarea_font: Fonte usada nas áreas de texto
label_font_default: Default font label_font_default: Fonte por defecto
label_font_monospace: Monospaced font label_font_monospace: Fonte Monospaced
label_font_proportional: Proportional font label_font_proportional: Fonte Proporcional
setting_timespan_format: Time span format setting_timespan_format: Formato de período temporal
label_table_of_contents: Table of contents label_table_of_contents: Índice de contidos
setting_commit_logs_formatting: Apply text formatting to commit messages setting_commit_logs_formatting: Aplicar formateo de texto para as mensaxes de commit
setting_mail_handler_enable_regex_delimiters: Enable regular expressions setting_mail_handler_enable_regex_delimiters: Activar expresións regulares
error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new error_move_of_child_not_possible: 'A subtarefa %{child} non se pode mover ao novo
project: %{errors}' proxecto: %{errors}'
error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: O tempo empregado non pode
be reassigned to an issue that is about to be deleted ser reasignado a unha petición que vai ser eliminada
setting_timelog_required_fields: Required fields for time logs setting_timelog_required_fields: Campos obrigatorios para as imputacións de tempo
label_attribute_of_object: '%{object_name}''s %{name}' label_attribute_of_object: '%{object_name}''s %{name}'
label_user_mail_option_only_assigned: Only for things I watch or I am assigned to label_user_mail_option_only_assigned: Só para as cousas que sigo ou que teño asignado
label_user_mail_option_only_owner: Only for things I watch or I am the owner of label_user_mail_option_only_owner: Só para as cousas que sigo ou son o propietario
warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion warning_fields_cleared_on_bulk_edit: Os cambios provocarán o borrado automático
of values from one or more fields on the selected objects dos valores de un ou máis campos dos obxectos seleccionados
field_updated_by: Updated by field_updated_by: Actualizado por
field_last_updated_by: Last updated by field_last_updated_by: Última actualización por
field_full_width_layout: Full width layout field_full_width_layout: Deseño para ancho completo
label_last_notes: Last notes label_last_notes: Últimas notas
field_digest: Checksum field_digest: Suma de verificación
field_default_assigned_to: Default assignee field_default_assigned_to: Asignado por defecto
setting_show_custom_fields_on_registration: Show custom fields on registration setting_show_custom_fields_on_registration: Amosar os campos personalizados no rexistro
permission_view_news: View news permission_view_news: Ver noticias
label_no_preview_alternative_html: No preview available. %{link} the file instead. label_no_preview_alternative_html: Non hai vista previa dispoñible. %{link} o ficheiro no seu lugar.
label_no_preview_download: Download label_no_preview_download: Descargar

View file

@ -618,7 +618,7 @@ ja:
one: 1コメント one: 1コメント
other: "%{count}コメント" other: "%{count}コメント"
label_comment_add: コメント追加 label_comment_add: コメント追加
label_comment_added: 追加されたコメント label_comment_added: コメントが追加されました
label_comment_delete: コメント削除 label_comment_delete: コメント削除
label_query: カスタムクエリ label_query: カスタムクエリ
label_query_plural: カスタムクエリ label_query_plural: カスタムクエリ

View file

@ -890,7 +890,7 @@ lt:
label_version_sharing_none: Nesidalinama label_version_sharing_none: Nesidalinama
label_version_sharing_descendants: Su sub-projektais label_version_sharing_descendants: Su sub-projektais
label_version_sharing_hierarchy: Su projekto hierarchija label_version_sharing_hierarchy: Su projekto hierarchija
label_version_sharing_tree: WiSu projekto medžiu label_version_sharing_tree: Su projekto medžiu
label_version_sharing_system: Su visais projektais label_version_sharing_system: Su visais projektais
label_update_issue_done_ratios: Atnaujinti darbo atlikimo progresą label_update_issue_done_ratios: Atnaujinti darbo atlikimo progresą
label_copy_source: Šaltinis label_copy_source: Šaltinis

View file

@ -149,9 +149,9 @@ pt-BR:
not_same_project: "não pertence ao mesmo projeto" not_same_project: "não pertence ao mesmo projeto"
circular_dependency: "Esta relação geraria uma dependência circular" circular_dependency: "Esta relação geraria uma dependência circular"
cant_link_an_issue_with_a_descendant: "Uma tarefa não pode ser relacionada a uma de suas subtarefas" cant_link_an_issue_with_a_descendant: "Uma tarefa não pode ser relacionada a uma de suas subtarefas"
earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" earlier_than_minimum_start_date: "não pode ser anterior a %{date} por causa de tarefas anteriores"
not_a_regexp: "is not a valid regular expression" not_a_regexp: "não é uma expressão regular válida"
open_issue_with_closed_parent: "An open issue cannot be attached to a closed parent task" open_issue_with_closed_parent: "Uma tarefa aberta não pode ser associada à uma tarefa pai fechada"
actionview_instancetag_blank_option: Selecione actionview_instancetag_blank_option: Selecione
@ -159,7 +159,7 @@ pt-BR:
general_text_Yes: 'Sim' general_text_Yes: 'Sim'
general_text_no: 'não' general_text_no: 'não'
general_text_yes: 'sim' general_text_yes: 'sim'
general_lang_name: 'Portuguese/Brasil (Português/Brasil)' general_lang_name: 'Portuguese/Brazil (Português/Brasil)'
general_csv_separator: ';' general_csv_separator: ';'
general_csv_decimal_separator: ',' general_csv_decimal_separator: ','
general_csv_encoding: ISO-8859-1 general_csv_encoding: ISO-8859-1
@ -1125,7 +1125,7 @@ pt-BR:
label_group_non_member: Usuários não membros label_group_non_member: Usuários não membros
label_add_projects: Adicionar projetos label_add_projects: Adicionar projetos
field_default_status: Situação padrão field_default_status: Situação padrão
text_subversion_repository_note: 'Examplos: file:///, http://, https://, svn://, svn+[tunnelscheme]://' text_subversion_repository_note: 'Exemplos: file:///, http://, https://, svn://, svn+[tunnelscheme]://'
field_users_visibility: Visibilidade do usuário field_users_visibility: Visibilidade do usuário
label_users_visibility_all: Todos usuários ativos label_users_visibility_all: Todos usuários ativos
label_users_visibility_members_of_visible_projects: Membros de projetos visíveis label_users_visibility_members_of_visible_projects: Membros de projetos visíveis

View file

@ -1166,7 +1166,7 @@ ru:
label_cross_project_system: Со всеми проектами label_cross_project_system: Со всеми проектами
button_hide: Скрыть button_hide: Скрыть
setting_non_working_week_days: Нерабочие дни setting_non_working_week_days: Нерабочие дни
label_in_the_next_days: в средующие дни label_in_the_next_days: в следующие дни
label_in_the_past_days: в прошлые дни label_in_the_past_days: в прошлые дни
label_attribute_of_user: Пользователь %{name} label_attribute_of_user: Пользователь %{name}
text_turning_multiple_off: Если отключить множественные значения, лишние значения из списка будут удалены, чтобы осталось только по одному значению. text_turning_multiple_off: Если отключить множественные значения, лишние значения из списка будут удалены, чтобы осталось только по одному значению.

View file

@ -134,7 +134,7 @@ sv:
not_same_project: "tillhör inte samma projekt" not_same_project: "tillhör inte samma projekt"
circular_dependency: "Denna relation skulle skapa ett cirkulärt beroende" circular_dependency: "Denna relation skulle skapa ett cirkulärt beroende"
cant_link_an_issue_with_a_descendant: "Ett ärende kan inte länkas till ett av dess underärenden" cant_link_an_issue_with_a_descendant: "Ett ärende kan inte länkas till ett av dess underärenden"
earlier_than_minimum_start_date: "kan inte vara tidigare än% {datum} på grund av föregående ärenden" earlier_than_minimum_start_date: "kan inte vara tidigare än %{date} på grund av föregående ärenden"
not_a_regexp: "is not a valid regular expression" not_a_regexp: "is not a valid regular expression"
open_issue_with_closed_parent: "An open issue cannot be attached to a closed parent task" open_issue_with_closed_parent: "An open issue cannot be attached to a closed parent task"

View file

@ -1215,12 +1215,12 @@ uk:
field_updated_by: Оновлено field_updated_by: Оновлено
field_last_updated_by: Востаннє оновлено field_last_updated_by: Востаннє оновлено
field_full_width_layout: Макет на повну ширину field_full_width_layout: Макет на повну ширину
label_user_mail_option_only_assigned: Only for things I watch or I am assigned to label_user_mail_option_only_assigned: Лише для об'єктів за якими я спостерігаю або до яких прикріплений
label_user_mail_option_only_owner: Only for things I watch or I am the owner of label_user_mail_option_only_owner: Лише для об'єктів за якими я спостерігаю або є власником
label_last_notes: Last notes label_last_notes: Останні коментарі
field_digest: Checksum field_digest: Контрольна сума
field_default_assigned_to: Default assignee field_default_assigned_to: Призначити по замовчуванню
setting_show_custom_fields_on_registration: Show custom fields on registration setting_show_custom_fields_on_registration: Показувати додаткові поля при реєстрації
permission_view_news: View news permission_view_news: Переглядати новини
label_no_preview_alternative_html: No preview available. %{link} the file instead. label_no_preview_alternative_html: Попередній перегляд недоступний. %{link} файл натомість.
label_no_preview_download: Download label_no_preview_download: Завантажити

View file

@ -304,7 +304,7 @@ zh:
field_comments_sorting: 显示注释 field_comments_sorting: 显示注释
field_parent_title: 上级页面 field_parent_title: 上级页面
field_editable: 可编辑 field_editable: 可编辑
field_watcher: 跟踪 field_watcher: 关注
field_identity_url: OpenID URL field_identity_url: OpenID URL
field_content: 内容 field_content: 内容
field_group_by: 根据此条件分组 field_group_by: 根据此条件分组
@ -393,9 +393,9 @@ zh:
permission_save_queries: 保存查询 permission_save_queries: 保存查询
permission_view_gantt: 查看甘特图 permission_view_gantt: 查看甘特图
permission_view_calendar: 查看日历 permission_view_calendar: 查看日历
permission_view_issue_watchers: 查看跟踪者列表 permission_view_issue_watchers: 查看关注者列表
permission_add_issue_watchers: 添加跟踪 permission_add_issue_watchers: 添加关注
permission_delete_issue_watchers: 删除跟踪 permission_delete_issue_watchers: 删除关注
permission_log_time: 登记工时 permission_log_time: 登记工时
permission_view_time_entries: 查看耗时 permission_view_time_entries: 查看耗时
permission_edit_time_entries: 编辑耗时 permission_edit_time_entries: 编辑耗时
@ -675,7 +675,7 @@ zh:
label_options: 选项 label_options: 选项
label_copy_workflow_from: 从以下选项复制工作流程 label_copy_workflow_from: 从以下选项复制工作流程
label_permissions_report: 权限报表 label_permissions_report: 权限报表
label_watched_issues: 跟踪的问题 label_watched_issues: 关注的问题
label_related_issues: 相关的问题 label_related_issues: 相关的问题
label_applied_status: 应用后的状态 label_applied_status: 应用后的状态
label_loading: 载入中... label_loading: 载入中...
@ -731,7 +731,7 @@ zh:
label_user_mail_option_all: "收取我的项目的所有通知" label_user_mail_option_all: "收取我的项目的所有通知"
label_user_mail_option_selected: "收取选中项目的所有通知..." label_user_mail_option_selected: "收取选中项目的所有通知..."
label_user_mail_option_none: "不收取任何通知" label_user_mail_option_none: "不收取任何通知"
label_user_mail_option_only_my_events: "只收取我跟踪或参与的项目的通知" label_user_mail_option_only_my_events: "只收取我关注或参与的项目的通知"
label_user_mail_no_self_notified: "不要发送对我自己提交的修改的通知" label_user_mail_no_self_notified: "不要发送对我自己提交的修改的通知"
label_registration_activation_by_email: 通过邮件认证激活帐号 label_registration_activation_by_email: 通过邮件认证激活帐号
label_registration_manual_activation: 手动激活帐号 label_registration_manual_activation: 手动激活帐号
@ -751,7 +751,7 @@ zh:
label_reverse_chronological_order: 按时间顺序(倒序) label_reverse_chronological_order: 按时间顺序(倒序)
label_incoming_emails: 接收邮件 label_incoming_emails: 接收邮件
label_generate_key: 生成一个key label_generate_key: 生成一个key
label_issue_watchers: 跟踪 label_issue_watchers: 关注
label_example: 示例 label_example: 示例
label_display: 显示 label_display: 显示
label_sort: 排序 label_sort: 排序
@ -811,8 +811,8 @@ zh:
button_sort: 排序 button_sort: 排序
button_log_time: 登记工时 button_log_time: 登记工时
button_rollback: 恢复到这个版本 button_rollback: 恢复到这个版本
button_watch: 跟踪 button_watch: 关注
button_unwatch: 取消跟踪 button_unwatch: 取消关注
button_reply: 回复 button_reply: 回复
button_archive: 存档 button_archive: 存档
button_unarchive: 取消存档 button_unarchive: 取消存档
@ -866,7 +866,7 @@ zh:
text_issue_category_destroy_question: "有一些问题(%{count} 个)属于此类别。您想进行哪种操作?" text_issue_category_destroy_question: "有一些问题(%{count} 个)属于此类别。您想进行哪种操作?"
text_issue_category_destroy_assignments: 删除问题的所属类别(问题变为无类别) text_issue_category_destroy_assignments: 删除问题的所属类别(问题变为无类别)
text_issue_category_reassign_to: 为问题选择其它类别 text_issue_category_reassign_to: 为问题选择其它类别
text_user_mail_option: "对于没有选中的项目,您将只会收到您跟踪或参与的项目的通知(比如说,您是问题的报告者, 或被指派解决此问题)。" text_user_mail_option: "对于没有选中的项目,您将只会收到您关注或参与的项目的通知(比如说,您是问题的报告者, 或被指派解决此问题)。"
text_no_configuration_data: "角色、跟踪标签、问题状态和工作流程还没有设置。\n强烈建议您先载入默认设置然后在此基础上进行修改。" text_no_configuration_data: "角色、跟踪标签、问题状态和工作流程还没有设置。\n强烈建议您先载入默认设置然后在此基础上进行修改。"
text_load_default_configuration: 载入默认设置 text_load_default_configuration: 载入默认设置
text_status_changed_by_changeset: "已应用到变更列表 %{value}." text_status_changed_by_changeset: "已应用到变更列表 %{value}."
@ -1008,7 +1008,7 @@ zh:
text_issue_conflict_resolution_cancel: 取消我所有的变更并重新刷新显示 %{link} 。 text_issue_conflict_resolution_cancel: 取消我所有的变更并重新刷新显示 %{link} 。
permission_manage_related_issues: 相关问题管理 permission_manage_related_issues: 相关问题管理
field_auth_source_ldap_filter: LDAP 过滤器 field_auth_source_ldap_filter: LDAP 过滤器
label_search_for_watchers: 通过查找方式添加跟踪 label_search_for_watchers: 通过查找方式添加关注
notice_account_deleted: 您的账号已被永久删除(账号已无法恢复)。 notice_account_deleted: 您的账号已被永久删除(账号已无法恢复)。
setting_unsubscribe: 允许用户退订 setting_unsubscribe: 允许用户退订
button_delete_my_account: 删除我的账号 button_delete_my_account: 删除我的账号

View file

@ -4,6 +4,340 @@ Redmine - project management software
Copyright (C) 2006-2017 Jean-Philippe Lang Copyright (C) 2006-2017 Jean-Philippe Lang
http://www.redmine.org/ http://www.redmine.org/
== 2019-12-20 v3.4.13
=== [Attachments]
* Defect #20277: "Couldn't find template for digesting" error in the log when sending a thumbnail or an attachment
=== [Gems support]
* Patch #32592: Require 'mocha/minitest' instead of deprecated 'mocha/setup'
=== [Text formatting]
* Patch #25742: Improper markup sanitization in user content for space separated attribute values and different quoting styles
== 2019-10-19 v3.4.12
=== [Code cleanup/refactoring]
* Defect #32022: IssueSubtaskingTest fails with high probability
=== [Documentation]
* Defect #32170: Text enclosed in pre tag in Wiki formatting reference is not displayed in monospaced font in Chrome
* Defect #32184: Incorrect headings example in Textile help
=== [Gems support]
* Defect #32300: Don't use sprockets 4.0.0 in order to avoid Sprockets::Railtie::ManifestNeededError
* Patch #32294: Update ruby-openid to 2.9.2
=== [Issues]
* Defect #31778: Total estimated time issue query column and issue field might leak information
=== [Issues list]
* Defect #31779: Total estimated time column shown even when estimated time field is deactivated
=== [Translations]
* Defect #32290: Typo in Russian translation for label_in_the_next_days
=== [UI]
* Defect #32012: Broken JavaScript icon in the repository view
* Defect #32024: Broken gzip icon in the repository view
== 2019-06-10 v3.4.11
=== [Administration]
* Defect #31125: Don't output ImageMagick version information to stdout
=== [Code cleanup/refactoring]
* Defect #30811: "rake db:fixtures:load" does not work
=== [Email receiving]
* Defect #30457: MailHandler.safe_receive does not output any error log
* Defect #31503: Undefined local variable sender_email in MailHandler#receive_message_reply
=== [Issues filter]
* Patch #31276: Serialize group_by and totalable_names in Query#as_params
=== [SCM]
* Defect #31120: Garbage lines in the output of 'git branch' break git adapter
=== [Security]
* Defect #31520: Persistent XSS in textile formatting
=== [Translations]
* Defect #31264: Conflicting translation between "track" and "watch" in Simplified Chinese
=== [UI - Responsive]
* Defect #31153: Display horizontal scroll bar of files table when overflow occurs on small screen
* Defect #31311: admin/info page: text cut off in pre tag on mobile
=== [Wiki]
* Patch #31334: Do not lose content when updating a wiki page that has been renamed in the meantime
== 2019-03-31 v3.4.10
=== [Administration]
* Defect #30939: Timeout for "Check for updates" on Plugins page is too short
=== [Files]
* Defect #31087: Deleting a version silently deletes its attachments
=== [Issues filter]
* Defect #30367: "Last updated by" filter causes an SQL error with MariaDB
=== [REST API]
* Defect #29055: Searching for issue number with REST API redirects to issue HTML page
=== [Rails support]
* Feature #31027: Upgrade to Rails 4.2.11.1
=== [Search engine]
* Defect #30923: Project search should select subprojects scope when the project has subprojects
=== [UI]
* Defect #30872: Copyright is outdated
== 2019-02-21 v3.4.9
=== [Gems support]
* Defect #30114: Installing xpath with Bundler fails in Ruby <=2.2
* Patch #30821: Stay in RMagick 2.16.0 and don't update to 3.0.0
=== [UI]
* Patch #30818: Issues autocomplete should respond with content type json
== 2019-01-20 v3.4.8
=== [Code cleanup/refactoring]
* Patch #30413: Add ".ruby-version" to svn:ignore, .git:ignore, and .hgignore
=== [Database]
* Defect #30171: Decrypting LDAP and SCM passwords fail if the plaintext password is longer than 31 bytes
=== [Gems support]
* Defect #30353: Installing rails with Bundler 2.0 fails in 3.x
=== [Importers]
* Patch #30412: Import UTF-8 issue CSV files with BOM and quoted strings
=== [Translations]
* Patch #30293: Ukrainian translation update for 3.4-stable
=== [UI]
* Defect #30426: Table rows are not highlighted on mouseover on some pages
* Patch #29951: Quick design fix/proposals for projects index page
== 2018-12-09 v3.4.7
=== [Custom fields]
* Defect #8317: Strip whitespace from integer custom field
* Defect #28925: Custom field values for enumerations not saved
* Patch #29674: Missing validation for custom field formats based on RecordList
=== [Email receiving]
* Defect #28576: Attachments are added even if validation fails when updating an issue via email
* Defect #29191: Cannot set no_notification option when receiving emails via IMAP or POP3
=== [Importers]
* Defect #30001: CSV importer ignores shared version names of other projects
=== [Issues]
* Defect #28946: If assignee is locked subtasks don't get copied
* Defect #30009: Empty sort criteria for issue query gives error
* Defect #30027: Some styles (for ex: borders for tables) in a custom field with text formatting enabled are not displayed
=== [Issues filter]
* Defect #26785: Wrong columns after CSV export
=== [PDF export]
* Defect #28125: PNG images on a wiki page don't appear in exported PDF
* Defect #28565: PDF export has too many whitespaces
=== [REST API]
* Defect #20788: REST API with JSON content missing attributes with false values
=== [Rails support]
* Feature #30043: Update Rails to 4.2.11
=== [SCM]
* Defect #29413: Mercurial 4.7 compatibility
=== [Search engine]
* Defect #28636: Cannot find an issue from a closed subproject when search scope is Project and its subprojects
=== [Text formatting]
* Defect #8395: Tags start with 'pre' are handled as 'pre' tag in Textile
* Defect #29038: Thumbnail macro causes attachment file not found and broken filename and link
* Defect #29247: Textile phrase modifiers break wiki macros
* Defect #29756: \f or \v character in Textile markup may cause RegexpError exception
=== [Time tracking]
* Patch #29308: Time entry creation: preserve 'spent on' value when using 'Create and Continue'
=== [Translations]
* Patch #29702: Brazilian wiki help translation update
* Patch #29703: Brazilian translation (jstoolbar-pt-br.js) update
* Patch #29718: Brazilian translation update for 3.4-stable
* Patch #29735: Galician translation fix for the words empty, blank, and key
* Patch #29736: Galician translation update for 3.4-stable
=== [UI]
* Defect #29918: Related issues section ignores the date format setting
* Defect #29950: Fix list rendering inside project description in projects#index
=== [UI - Responsive]
* Defect #24309: Setting page for repository does not scroll horizontally on small screens
=== [Wiki]
* Feature #29791: Hide "Files" section in wiki pages when printing
== 2018-06-10 v3.4.6
=== [Issues]
* Defect #27863: If version is closed or locked subtasks don't get copied
* Defect #28765: Copying an issue fails if the issue is watched by a locked user
* Patch #28649: Log automatic rescheduling of following issues to journal
=== [Permissions and roles]
* Defect #28693: Irrelevant permission is required to access some tabs in project settings page
=== [Project settings]
* Defect #27122: Filter for version name should be case-insensitive
=== [SCM]
* Defect #28725: Mercurial 4.6 compatibility
=== [Text formatting]
* Defect #28469: Syntax highlighter does not work if language name is single-quoted
=== [Translations]
* Patch #28881: Fix Japanese mistranslation for label_comment_added
=== [UI]
* Defect #22023: Issue id input should get focus after adding related issue
=== [UI - Responsive]
* Defect #28523: Display horizontal scroll bar of plugins table when overflow occurs on small screen
=== [Wiki]
* Patch #27090: Show the number of attachments on wiki pages
== 2018-04-07 v3.4.5
=== [Custom fields]
* Defect #28393: Sort issue custom fields by position in tracker UI
=== [Email notifications]
* Defect #28302: Security notification when changing password on password forgotten is empty
=== [Gantt]
* Defect #28204: Too large avatar breaks gantt when assignee is a group
=== [Issues]
* Defect #27862: Preformatted text overflows in preview
* Patch #28168: Allow context-menu edit of % done and priority of parent issues if the fields are not derived
=== [Issues filter]
* Defect #28180: Role-base cross-project issue query visibility calculated incorrectly
=== [Plugin API]
* Patch #27963: Remove 'unloadable' from bundled sample plugin
=== [Security]
* Defect #26857: Fix for CVE-2015-9251 in JQuery 1.11.1
=== [Text formatting]
* Defect #27884: RTL wiki class broken in Redmine 3.2.6
* Defect #28331: h4, h5 and h6 headings on wiki pages should have a paragraph mark
* Patch #28119: Enable lax_spacing for markdown formatting in order to allow markdown blocks not surrounded by empty lines
=== [Time tracking]
* Defect #28110: Don't allow reassigning reported hours to the project if issue is a required field for time logs
=== [Translations]
* Defect #28109: Incorrect interpolation in Swedish locale
* Defect #28113: Fix typo in German label_font_default
* Defect #28192: Fix typo in German label_font_monospace
* Patch #27994: Galician translation update (jstoolbar-gl.js)
* Patch #28102: Fix typo in Lithuanian label_version_sharing_tree
=== [UI]
* Defect #28079: The green tick is positioned after the label in the new member modals
* Defect #28208: Anonymous icon is wrongly displayed when assignee is a group
* Defect #28259: attachments_fields id to class change not properly reflected in all CSS
=== [Wiki]
* Defect #25299: Markdown pre-block could derive incorrect wiki sections
== 2018-01-08 v3.4.4 == 2018-01-08 v3.4.4
=== [Accounts / authentication] === [Accounts / authentication]

View file

@ -1,7 +1,5 @@
# Sample plugin controller # Sample plugin controller
class ExampleController < ApplicationController class ExampleController < ApplicationController
unloadable
layout 'base' layout 'base'
before_action :find_project, :authorize before_action :find_project, :authorize
menu_item :sample_plugin menu_item :sample_plugin

View file

@ -126,7 +126,7 @@ Redmine::AccessControl.map do |map|
map.permission :log_time, {:timelog => [:new, :create]}, :require => :loggedin map.permission :log_time, {:timelog => [:new, :create]}, :require => :loggedin
map.permission :edit_time_entries, {:timelog => [:edit, :update, :destroy, :bulk_edit, :bulk_update]}, :require => :member map.permission :edit_time_entries, {:timelog => [:edit, :update, :destroy, :bulk_edit, :bulk_update]}, :require => :member
map.permission :edit_own_time_entries, {:timelog => [:edit, :update, :destroy,:bulk_edit, :bulk_update]}, :require => :loggedin map.permission :edit_own_time_entries, {:timelog => [:edit, :update, :destroy,:bulk_edit, :bulk_update]}, :require => :loggedin
map.permission :manage_project_activities, {:project_enumerations => [:update, :destroy]}, :require => :member map.permission :manage_project_activities, {:projects => :settings, :project_enumerations => [:update, :destroy]}, :require => :member
end end
map.project_module :news do |map| map.project_module :news do |map|
@ -164,7 +164,7 @@ Redmine::AccessControl.map do |map|
map.permission :browse_repository, {:repositories => [:show, :browse, :entry, :raw, :annotate, :changes, :diff, :stats, :graph]}, :read => true map.permission :browse_repository, {:repositories => [:show, :browse, :entry, :raw, :annotate, :changes, :diff, :stats, :graph]}, :read => true
map.permission :commit_access, {} map.permission :commit_access, {}
map.permission :manage_related_issues, {:repositories => [:add_related_issue, :remove_related_issue]} map.permission :manage_related_issues, {:repositories => [:add_related_issue, :remove_related_issue]}
map.permission :manage_repository, {:repositories => [:new, :create, :edit, :update, :committers, :destroy]}, :require => :member map.permission :manage_repository, {:projects => :settings, :repositories => [:new, :create, :edit, :update, :committers, :destroy]}, :require => :member
end end
map.project_module :boards do |map| map.project_module :boards do |map|
@ -174,7 +174,7 @@ Redmine::AccessControl.map do |map|
map.permission :edit_own_messages, {:messages => :edit, :attachments => :upload}, :require => :loggedin map.permission :edit_own_messages, {:messages => :edit, :attachments => :upload}, :require => :loggedin
map.permission :delete_messages, {:messages => :destroy}, :require => :member map.permission :delete_messages, {:messages => :destroy}, :require => :member
map.permission :delete_own_messages, {:messages => :destroy}, :require => :loggedin map.permission :delete_own_messages, {:messages => :destroy}, :require => :loggedin
map.permission :manage_boards, {:boards => [:new, :create, :edit, :update, :destroy]}, :require => :member map.permission :manage_boards, {:projects => :settings, :boards => [:new, :create, :edit, :update, :destroy]}, :require => :member
end end
map.project_module :calendar do |map| map.project_module :calendar do |map|

View file

@ -33,7 +33,7 @@ module Redmine
c.iv = iv c.iv = iv
e = c.update(text.to_s) e = c.update(text.to_s)
e << c.final e << c.final
"aes-256-cbc:" + [e, iv].map {|v| Base64.encode64(v).strip}.join('--') "aes-256-cbc:" + [e, iv].map {|v| Base64.strict_encode64(v)}.join('--')
end end
end end

View file

@ -486,7 +486,7 @@ module Redmine
def validate_single_value(custom_field, value, customized=nil) def validate_single_value(custom_field, value, customized=nil)
errs = super errs = super
errs << ::I18n.t('activerecord.errors.messages.not_a_number') unless value.to_s =~ /^[+-]?\d+$/ errs << ::I18n.t('activerecord.errors.messages.not_a_number') unless value.to_s.strip =~ /^[+-]?\d+$/
errs errs
end end
@ -736,6 +736,16 @@ module Redmine
options options
end end
def validate_custom_value(custom_value)
values = Array.wrap(custom_value.value).reject {|value| value.to_s == ''}
invalid_values = values - possible_custom_value_options(custom_value).map(&:last)
if invalid_values.any?
[::I18n.t('activerecord.errors.messages.inclusion')]
else
[]
end
end
def order_statement(custom_field) def order_statement(custom_field)
if target_class.respond_to?(:fields_for_order_statement) if target_class.respond_to?(:fields_for_order_statement)
target_class.fields_for_order_statement(value_join_alias(custom_field)) target_class.fields_for_order_statement(value_join_alias(custom_field))

View file

@ -81,7 +81,7 @@ module Redmine
# below : list unreadable files, but dont link them. # below : list unreadable files, but dont link them.
:path => utf_8_path, :path => utf_8_path,
:kind => (File.directory?(t1) ? 'dir' : 'file'), :kind => (File.directory?(t1) ? 'dir' : 'file'),
:size => (File.directory?(t1) ? nil : [File.size(t1)].pack('l').unpack('L').first), :size => (File.directory?(t1) ? nil : File.size(t1)),
:lastrev => :lastrev =>
Revision.new({:time => (File.mtime(t1)) }) Revision.new({:time => (File.mtime(t1)) })
}) })

View file

@ -83,6 +83,7 @@ module Redmine
io.each_line do |line| io.each_line do |line|
branch_rev = line.match('\s*(\*?)\s*(.*?)\s*([0-9a-f]{40}).*$') branch_rev = line.match('\s*(\*?)\s*(.*?)\s*([0-9a-f]{40}).*$')
bran = GitBranch.new(branch_rev[2]) bran = GitBranch.new(branch_rev[2])
next unless branch_rev
bran.revision = branch_rev[3] bran.revision = branch_rev[3]
bran.scmid = branch_rev[3] bran.scmid = branch_rev[3]
bran.is_default = ( branch_rev[1] == '*' ) bran.is_default = ( branch_rev[1] == '*' )

View file

@ -46,14 +46,22 @@ Output example of rhmanifest::
</rhmanifest> </rhmanifest>
""" """
import re, time, cgi, urllib import re, time, cgi, urllib
from mercurial import cmdutil, commands, node, error, hg from mercurial import cmdutil, commands, node, error, hg, registrar
cmdtable = {} cmdtable = {}
command = cmdutil.command(cmdtable) command = registrar.command(cmdtable) if hasattr(registrar, 'command') else cmdutil.command(cmdtable)
_x = cgi.escape _x = cgi.escape
_u = lambda s: cgi.escape(urllib.quote(s)) _u = lambda s: cgi.escape(urllib.quote(s))
def _changectx(repo, rev):
if isinstance(rev, str):
rev = repo.lookup(rev)
if hasattr(repo, 'changectx'):
return repo.changectx(rev)
else:
return repo[rev]
def _tip(ui, repo): def _tip(ui, repo):
# see mercurial/commands.py:tip # see mercurial/commands.py:tip
def tiprev(): def tiprev():
@ -61,7 +69,7 @@ def _tip(ui, repo):
return len(repo) - 1 return len(repo) - 1
except TypeError: # Mercurial < 1.1 except TypeError: # Mercurial < 1.1
return repo.changelog.count() - 1 return repo.changelog.count() - 1
tipctx = repo.changectx(tiprev()) tipctx = _changectx(repo, tiprev())
ui.write('<tip revision="%d" node="%s"/>\n' ui.write('<tip revision="%d" node="%s"/>\n'
% (tipctx.rev(), _x(node.hex(tipctx.node())))) % (tipctx.rev(), _x(node.hex(tipctx.node()))))
@ -94,13 +102,18 @@ def _branches(ui, repo):
return repo.branchheads(branch, closed=False) return repo.branchheads(branch, closed=False)
except TypeError: # Mercurial < 1.2 except TypeError: # Mercurial < 1.2
return repo.branchheads(branch) return repo.branchheads(branch)
def lookup(rev, n):
try:
return repo.lookup(rev)
except RuntimeError:
return n
for t, n, r in sorted(iterbranches(), key=lambda e: e[2], reverse=True): for t, n, r in sorted(iterbranches(), key=lambda e: e[2], reverse=True):
if repo.lookup(r) in branchheads(t): if lookup(r, n) in branchheads(t):
ui.write('<branch revision="%d" node="%s" name="%s"/>\n' ui.write('<branch revision="%d" node="%s" name="%s"/>\n'
% (r, _x(node.hex(n)), _x(t))) % (r, _x(node.hex(n)), _x(t)))
def _manifest(ui, repo, path, rev): def _manifest(ui, repo, path, rev):
ctx = repo.changectx(rev) ctx = _changectx(repo, rev)
ui.write('<manifest revision="%d" path="%s">\n' ui.write('<manifest revision="%d" path="%s">\n'
% (ctx.rev(), _u(path))) % (ctx.rev(), _u(path)))
@ -155,7 +168,7 @@ def rhdiff(ui, repo, *pats, **opts):
"""diff repository (or selected files)""" """diff repository (or selected files)"""
change = opts.pop('change', None) change = opts.pop('change', None)
if change: # add -c option for Mercurial<1.1 if change: # add -c option for Mercurial<1.1
base = repo.changectx(change).parents()[0].rev() base = _changectx(repo, change).parents()[0].rev()
opts['rev'] = [str(base), change] opts['rev'] = [str(base), change]
opts['nodates'] = True opts['nodates'] = True
return commands.diff(ui, repo, *map(urllib.unquote_plus, pats), **opts) return commands.diff(ui, repo, *map(urllib.unquote_plus, pats), **opts)

View file

@ -87,6 +87,7 @@ module Redmine
private private
def normalize! def normalize!
self.reject! {|s| s.first.blank? }
self.collect! {|s| s = Array(s); [s.first, (s.last == false || s.last.to_s == 'desc') ? 'desc' : 'asc']} self.collect! {|s| s = Array(s); [s.first, (s.last == false || s.last.to_s == 'desc') ? 'desc' : 'asc']}
self.slice!(3) self.slice!(3)
self self

View file

@ -49,7 +49,12 @@ module Redmine
def self.convert_available? def self.convert_available?
return @convert_available if defined?(@convert_available) return @convert_available if defined?(@convert_available)
@convert_available = system("#{shell_quote CONVERT_BIN} -version") rescue false begin
`#{shell_quote CONVERT_BIN} -version`
@convert_available = $?.success?
rescue
@convert_available = false
end
logger.warn("Imagemagick's convert binary (#{CONVERT_BIN}) not available") unless @convert_available logger.warn("Imagemagick's convert binary (#{CONVERT_BIN}) not available") unless @convert_available
@convert_available @convert_available
end end

View file

@ -4,7 +4,7 @@ module Redmine
module VERSION #:nodoc: module VERSION #:nodoc:
MAJOR = 3 MAJOR = 3
MINOR = 4 MINOR = 4
TINY = 4 TINY = 13
# Branch values: # Branch values:
# * official release: nil # * official release: nil

View file

@ -48,7 +48,7 @@ module Redmine
end end
def method_missing(sym, *args, &block) def method_missing(sym, *args, &block)
if args.any? if args.count > 0
if args.first.is_a?(Hash) if args.first.is_a?(Hash)
if @struct.last.is_a?(Array) if @struct.last.is_a?(Array)
@struct.last << args.first unless block @struct.last << args.first unless block

View file

@ -94,15 +94,13 @@ module Redmine
i = 0 i = 0
l = 1 l = 1
inside_pre = false inside_pre = false
@text.split(/(^(?:.+\r?\n\r?(?:\=+|\-+)|#+.+|~~~.*)\s*$)/).each do |part| @text.split(/(^(?:.+\r?\n\r?(?:\=+|\-+)|#+.+|(?:~~~|```).*)\s*$)/).each do |part|
level = nil level = nil
if part =~ /\A~{3,}(\S+)?\s*$/ if part =~ /\A(~{3,}|`{3,})(\S+)?\s*$/
if $1
if !inside_pre if !inside_pre
inside_pre = true inside_pre = true
end elsif !$2
else inside_pre = false
inside_pre = !inside_pre
end end
elsif inside_pre elsif inside_pre
# nop # nop
@ -142,7 +140,8 @@ module Redmine
:strikethrough => true, :strikethrough => true,
:superscript => true, :superscript => true,
:no_intra_emphasis => true, :no_intra_emphasis => true,
:footnotes => true :footnotes => true,
:lax_spacing => true
) )
end end
end end

View file

@ -120,9 +120,10 @@ module Redmine
## replace <pre> content ## replace <pre> content
text.gsub!(/<redpre#(\d+)>/) do text.gsub!(/<redpre#(\d+)>/) do
content = @pre_list[$1.to_i] content = @pre_list[$1.to_i]
if content.match(/<code\s+class="(\w+)">\s?(.+)/m) # This regex must match any data produced by RedCloth3#rip_offtags
language = $1 if content.match(/<code\s+class=(?:"([^"]+)"|'([^']+)')>\s?(.*)/m)
text = $2 language = $1 || $2
text = $3
if Redmine::SyntaxHighlighting.language_supported?(language) if Redmine::SyntaxHighlighting.language_supported?(language)
content = "<code class=\"#{language} syntaxhl\">" + content = "<code class=\"#{language} syntaxhl\">" +
Redmine::SyntaxHighlighting.highlight_by_language(text, language) Redmine::SyntaxHighlighting.highlight_by_language(text, language)

View file

@ -343,7 +343,7 @@ class RedCloth3 < String
A_VLGN = /[\-^~]/ A_VLGN = /[\-^~]/
C_CLAS = '(?:\([^")]+\))' C_CLAS = '(?:\([^")]+\))'
C_LNGE = '(?:\[[a-z\-_]+\])' C_LNGE = '(?:\[[a-z\-_]+\])'
C_STYL = '(?:\{[^"}]+\})' C_STYL = '(?:\{[^{][^"}]+\})'
S_CSPN = '(?:\\\\\d+)' S_CSPN = '(?:\\\\\d+)'
S_RSPN = '(?:/\d+)' S_RSPN = '(?:/\d+)'
A = "(?:#{A_HLGN}?#{A_VLGN}?|#{A_VLGN}?#{A_HLGN}?)" A = "(?:#{A_HLGN}?#{A_VLGN}?|#{A_VLGN}?#{A_HLGN}?)"
@ -1034,7 +1034,7 @@ class RedCloth3 < String
def flush_left( text ) def flush_left( text )
indt = 0 indt = 0
if text =~ /^ / if text =~ /^ /
while text !~ /^ {#{indt}}\S/ while text !~ /^ {#{indt}}[^ ]/
indt += 1 indt += 1
end unless text.empty? end unless text.empty?
if indt.nonzero? if indt.nonzero?
@ -1049,7 +1049,7 @@ class RedCloth3 < String
end end
OFFTAGS = /(code|pre|kbd|notextile)/ OFFTAGS = /(code|pre|kbd|notextile)/
OFFTAG_MATCH = /(?:(<\/#{ OFFTAGS }>)|(<#{ OFFTAGS }[^>]*>))(.*?)(?=<\/?#{ OFFTAGS }\W|\Z)/mi OFFTAG_MATCH = /(?:(<\/#{ OFFTAGS }\b>)|(<#{ OFFTAGS }\b[^>]*>))(.*?)(?=<\/?#{ OFFTAGS }\b\W|\Z)/mi
OFFTAG_OPEN = /<#{ OFFTAGS }/ OFFTAG_OPEN = /<#{ OFFTAGS }/
OFFTAG_CLOSE = /<\/?#{ OFFTAGS }/ OFFTAG_CLOSE = /<\/?#{ OFFTAGS }/
HASTAG_MATCH = /(<\/?\w[^\n]*?>)/m HASTAG_MATCH = /(<\/?\w[^\n]*?>)/m
@ -1213,7 +1213,13 @@ class RedCloth3 < String
ALLOWED_TAGS = %w(redpre pre code kbd notextile) ALLOWED_TAGS = %w(redpre pre code kbd notextile)
def escape_html_tags(text) def escape_html_tags(text)
text.gsub!(%r{<(\/?([!\w]+)[^<>\n]*)(>?)}) {|m| ALLOWED_TAGS.include?($2) ? "<#{$1}#{$3}" : "&lt;#{$1}#{'&gt;' unless $3.blank?}" } text.gsub!(%r{<(\/?([!\w]+)[^<>\n]*)(>?)}) do |m|
if ALLOWED_TAGS.include?($2) && $3.present?
"<#{$1}#{$3}"
else
"&lt;#{$1}#{'&gt;' unless $3.blank?}"
end
end
end end
end end

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -209,7 +209,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Nadpis 1. úrovně h1. Nadpis 1. úrovně
h2. Nadpis 2. úrovně h2. Nadpis 2. úrovně
h3. Nadpis 3. úrovně h3. Nadpis 3. úrovně
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Titre h1. Titre
h2. Sous-titre h2. Sous-titre
h3. Sous-sous-titre h3. Sous-sous-titre
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

View file

@ -200,7 +200,9 @@ http://www.redmine.org, someone@foo.bar
<pre> <pre>
h1. Heading h1. Heading
h2. Subheading h2. Subheading
h3. Subsubheading h3. Subsubheading
</pre> </pre>

Some files were not shown because too many files have changed in this diff Show more