diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..998308af4 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,10 @@ + +____________________________________________________________________ + +**Do not send a pull request to this GitHub repository**. + +For more detail, please see [official website] wiki [Contribute]. + +[official website]: http://www.redmine.org +[Contribute]: http://www.redmine.org/projects/redmine/wiki/Contribute + diff --git a/.gitignore b/.gitignore index 173b030c2..e19e36c2d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /.project +/.idea /.loadpath /.powrc /.rvmrc diff --git a/.hgignore b/.hgignore index cbc028a94..e1e895da3 100644 --- a/.hgignore +++ b/.hgignore @@ -1,6 +1,7 @@ syntax: glob .project +.idea .loadpath .powrc .rvmrc diff --git a/Gemfile b/Gemfile index b54c6ed4f..cb5646561 100644 --- a/Gemfile +++ b/Gemfile @@ -11,26 +11,24 @@ if RUBY_VERSION < "2.1" end gem "jquery-rails", "~> 3.1.4" gem "coderay", "~> 1.1.1" -gem "builder", ">= 3.0.4" gem "request_store", "1.0.5" gem "mime-types", (RUBY_VERSION >= "2.0" ? "~> 3.0" : "~> 2.99") gem "protected_attributes" -gem "actionpack-action_caching" gem "actionpack-xml_parser" gem "roadie-rails", "~> 1.1.1" gem "roadie", "~> 3.2.1" gem "mimemagic" gem "mail", "~> 2.6.4" -gem "nokogiri", (RUBY_VERSION >= "2.1" ? "~> 1.7.2" : "~> 1.6.8") +gem "nokogiri", (RUBY_VERSION >= "2.1" ? "~> 1.8.1" : "~> 1.6.8") gem "i18n", "~> 0.7.0" gem "ffi", "1.9.14", :platforms => :mingw if RUBY_VERSION < "2.0" -# 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" # Windows does not include zoneinfo files, so bundle the tzinfo-data gem -gem 'tzinfo-data', platforms: [:mingw, :x64_mingw, :mswin, :jruby] +gem 'tzinfo-data', platforms: [:mingw, :x64_mingw, :mswin] gem "rbpdf", "~> 1.19.6" # Optional gem for LDAP authentication @@ -52,16 +50,10 @@ platforms :mri, :mingw, :x64_mingw do # Optional Markdown support, not for JRuby group :markdown do - gem "redcarpet", "~> 3.3.2" + gem "redcarpet", "~> 3.4.0" end end -platforms :jruby do - # jruby-openssl is bundled with JRuby 1.7.0 - gem "jruby-openssl" if Object.const_defined?(:JRUBY_VERSION) && JRUBY_VERSION < '1.7.0' - gem "activerecord-jdbc-adapter", "~> 1.3.2" -end - # Include database gems for the adapters found in the database # configuration file require 'erb' @@ -75,19 +67,13 @@ if File.exist?(database_file) case adapter when 'mysql2' gem "mysql2", "~> 0.4.6", :platforms => [:mri, :mingw, :x64_mingw] - gem "activerecord-jdbcmysql-adapter", :platforms => :jruby - when 'mysql' - gem "activerecord-jdbcmysql-adapter", :platforms => :jruby when /postgresql/ gem "pg", "~> 0.18.1", :platforms => [:mri, :mingw, :x64_mingw] - gem "activerecord-jdbcpostgresql-adapter", :platforms => :jruby when /sqlite3/ gem "sqlite3", (RUBY_VERSION < "2.0" && RUBY_PLATFORM =~ /mingw/ ? "1.3.12" : "~>1.3.12"), :platforms => [:mri, :mingw, :x64_mingw] - gem "jdbc-sqlite3", ">= 3.8.10.1", :platforms => :jruby - gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby when /sqlserver/ - gem "tiny_tds", "~> 0.6.2", :platforms => [:mri, :mingw, :x64_mingw] + gem "tiny_tds", (RUBY_VERSION >= "2.0" ? "~> 1.0.5" : "~> 0.7.0"), :platforms => [:mri, :mingw, :x64_mingw] gem "activerecord-sqlserver-adapter", :platforms => [:mri, :mingw, :x64_mingw] else warn("Unknown database adapter `#{adapter}` found in config/database.yml, use Gemfile.local to load your own database gems") @@ -110,8 +96,10 @@ group :test do gem "rails-dom-testing" gem "mocha" gem "simplecov", "~> 0.9.1", :require => false + # TODO: remove this after upgrading to Rails 5 + gem "test_after_commit", "~> 0.4.2" # For running UI tests - gem "capybara" + gem "capybara", '~> 2.13' gem "selenium-webdriver", "~> 2.53.4" end diff --git a/app/controllers/account_controller.rb b/app/controllers/account_controller.rb index 81d6a581a..5070295d2 100644 --- a/app/controllers/account_controller.rb +++ b/app/controllers/account_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -19,8 +19,10 @@ class AccountController < ApplicationController helper :custom_fields include CustomFieldsHelper + self.main_menu = false + # prevents login action to be filtered by check_if_login_required application scope filter - skip_before_filter :check_if_login_required, :check_password_change + skip_before_action :check_if_login_required, :check_password_change # Overrides ApplicationController#verify_authenticity_token to disable # token verification on openid callbacks @@ -40,7 +42,7 @@ class AccountController < ApplicationController end end rescue AuthSourceException => e - logger.error "An error occured when authenticating #{params[:username]}: #{e.message}" + logger.error "An error occurred when authenticating #{params[:username]}: #{e.message}" render_error :message => e.message end @@ -78,20 +80,25 @@ class AccountController < ApplicationController return end if request.post? - @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation] - if @user.save - @token.destroy - Mailer.password_updated(@user, { remote_ip: request.remote_ip }) - flash[:notice] = l(:notice_account_password_updated) - redirect_to signin_path - return + if @user.must_change_passwd? && @user.check_password?(params[:new_password]) + flash.now[:error] = l(:notice_new_password_must_be_different) + else + @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation] + @user.must_change_passwd = false + if @user.save + @token.destroy + Mailer.password_updated(@user, { remote_ip: request.remote_ip }) + flash[:notice] = l(:notice_account_password_updated) + redirect_to signin_path + return + end end end render :template => "account/password_recovery" return else if request.post? - email = params[:mail].to_s + email = params[:mail].to_s.strip user = User.find_by_mail(email) # user not found unless user @@ -131,7 +138,7 @@ class AccountController < ApplicationController user_params = params[:user] || {} @user = User.new @user.safe_attributes = user_params - @user.pref.attributes = params[:pref] if params[:pref] + @user.pref.safe_attributes = params[:pref] @user.admin = false @user.register if session[:auth_source_registration] @@ -145,7 +152,6 @@ class AccountController < ApplicationController redirect_to my_account_path end else - @user.login = params[:user][:login] unless user_params[:identity_url].present? && user_params[:password].blank? && user_params[:password_confirmation].blank? @user.password, @user.password_confirmation = user_params[:password], user_params[:password_confirmation] end @@ -274,13 +280,13 @@ class AccountController < ApplicationController end def set_autologin_cookie(user) - token = Token.create(:user => user, :action => 'autologin') + token = user.generate_autologin_token secure = Redmine::Configuration['autologin_cookie_secure'] if secure.nil? secure = request.ssl? end cookie_options = { - :value => token.value, + :value => token, :expires => 1.year.from_now, :path => (Redmine::Configuration['autologin_cookie_path'] || RedmineApp::Application.config.relative_url_root || '/'), :secure => secure, diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 32df00f97..f82f0110a 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,7 +17,7 @@ class ActivitiesController < ApplicationController menu_item :activity - before_filter :find_optional_project + before_action :find_optional_project accept_rss_auth :index def index diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb index 944e60ca3..dfc73c5ab 100644 --- a/app/controllers/admin_controller.rb +++ b/app/controllers/admin_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,13 +17,12 @@ class AdminController < ApplicationController layout 'admin' + self.main_menu = false menu_item :projects, :only => :projects menu_item :plugins, :only => :plugins menu_item :info, :only => :info - before_filter :require_admin - helper :sort - include SortHelper + before_action :require_admin def index @no_configuration_data = Redmine::DefaultData::Loader::no_data? @@ -34,7 +33,10 @@ class AdminController < ApplicationController scope = Project.status(@status).sorted scope = scope.like(params[:name]) if params[:name].present? - @projects = scope.to_a + + @project_count = scope.count + @project_pages = Paginator.new @project_count, per_page_option, params['page'] + @projects = scope.limit(@project_pages.per_page).offset(@project_pages.offset).to_a render :action => "projects", :layout => false if request.xhr? end @@ -72,7 +74,6 @@ class AdminController < ApplicationController end def info - @db_adapter_name = ActiveRecord::Base.connection.adapter_name @checklist = [ [:text_default_administrator_account_changed, User.default_admin_account_changed?], [:text_file_repository_writable, File.writable?(Attachment.storage_path)], diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 12ea3133e..1d42901f0 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -51,7 +51,7 @@ class ApplicationController < ActionController::Base end end - before_filter :session_expiration, :user_setup, :check_if_login_required, :set_localization, :check_password_change + before_action :session_expiration, :user_setup, :check_if_login_required, :set_localization, :check_password_change rescue_from ::Unauthorized, :with => :deny_access rescue_from ::ActionView::MissingTemplate, :with => :missing_template @@ -168,9 +168,10 @@ class ApplicationController < ActionController::Base # Logs out current user def logout_user if User.current.logged? - cookies.delete(autologin_cookie_name) - Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin']) - Token.delete_all(["user_id = ? AND action = ? AND value = ?", User.current.id, 'session', session[:tk]]) + if autologin = cookies.delete(autologin_cookie_name) + User.current.delete_autologin_token(autologin) + end + User.current.delete_session_token(session[:tk]) self.logged_user = nil end end @@ -213,7 +214,7 @@ class ApplicationController < ActionController::Base if !User.current.logged? # Extract only the basic url parameters on non-GET requests if request.get? - url = url_for(params) + url = request.original_url else url = url_for(:controller => params[:controller], :action => params[:action], :id => params[:id], :project_id => params[:project_id]) end @@ -369,7 +370,7 @@ class ApplicationController < ActionController::Base end # make sure that the user is a member of the project (or admin) if project is private - # used as a before_filter for actions that do not require any particular permission on the project + # used as a before_action for actions that do not require any particular permission on the project def check_project_privacy if @project && !@project.archived? if @project.visible? @@ -434,7 +435,7 @@ class ApplicationController < ActionController::Base return false end - if path.match(%r{/(login|account/register)}) + if path.match(%r{/(login|account/register|account/lost_password)}) return false end @@ -453,14 +454,16 @@ class ApplicationController < ActionController::Base # Redirects to the request referer if present, redirects to args or call block otherwise. def redirect_to_referer_or(*args, &block) - redirect_to :back - rescue ::ActionController::RedirectBackError - if args.any? - redirect_to *args - elsif block_given? - block.call + if referer = request.headers["Referer"] + redirect_to referer else - raise "#redirect_to_referer_or takes arguments or a block" + if args.any? + redirect_to *args + elsif block_given? + block.call + else + raise "#redirect_to_referer_or takes arguments or a block" + end end end @@ -642,20 +645,18 @@ class ApplicationController < ActionController::Base # Rescues an invalid query statement. Just in case... def query_statement_invalid(exception) logger.error "Query::StatementInvalid: #{exception.message}" if logger - session.delete(:query) - sort_clear if respond_to?(:sort_clear) + session.delete(:issue_query) render_error "An error occurred while executing the query and has been logged. Please report this error to your Redmine administrator." end - # Renders a 200 response for successfull updates or deletions via the API + # Renders a 200 response for successful updates or deletions via the API def render_api_ok render_api_head :ok end # Renders a head API response def render_api_head(status) - # #head would return a response body with one space - render :text => '', :status => status, :layout => nil + head status end # Renders API response on validation failure diff --git a/app/controllers/attachments_controller.rb b/app/controllers/attachments_controller.rb index a9bc5793a..1b18662ad 100644 --- a/app/controllers/attachments_controller.rb +++ b/app/controllers/attachments_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,17 +16,18 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class AttachmentsController < ApplicationController - before_filter :find_attachment, :only => [:show, :download, :thumbnail, :destroy] - before_filter :find_editable_attachments, :only => [:edit, :update] - before_filter :file_readable, :read_authorize, :only => [:show, :download, :thumbnail] - before_filter :delete_authorize, :only => :destroy - before_filter :authorize_global, :only => :upload + before_action :find_attachment, :only => [:show, :download, :thumbnail, :update, :destroy] + before_action :find_editable_attachments, :only => [:edit_all, :update_all] + before_action :file_readable, :read_authorize, :only => [:show, :download, :thumbnail] + before_action :update_authorize, :only => :update + before_action :delete_authorize, :only => :destroy + before_action :authorize_global, :only => :upload # Disable check for same origin requests for JS files, i.e. attachments with # MIME type text/javascript. - skip_after_filter :verify_same_origin_request, :only => :download + skip_after_action :verify_same_origin_request, :only => :download - accept_api_auth :show, :download, :thumbnail, :upload, :destroy + accept_api_auth :show, :download, :thumbnail, :upload, :update, :destroy def show respond_to do |format| @@ -77,7 +78,7 @@ class AttachmentsController < ApplicationController end else # No thumbnail for the attachment or thumbnail could not be created - render :nothing => true, :status => 404 + head 404 end end @@ -85,7 +86,7 @@ class AttachmentsController < ApplicationController # Make sure that API users get used to set this content type # as it won't trigger Rails' automatic parsing of the request body for parameters unless request.content_type == 'application/octet-stream' - render :nothing => true, :status => 406 + head 406 return end @@ -107,17 +108,32 @@ class AttachmentsController < ApplicationController end end - def edit + # Edit all the attachments of a container + def edit_all + end + + # Update all the attachments of a container + def update_all + if Attachment.update_attachments(@attachments, update_all_params) + redirect_back_or_default home_path + return + end + render :action => 'edit_all' end def update - if params[:attachments].is_a?(Hash) - if Attachment.update_attachments(@attachments, params[:attachments]) - redirect_back_or_default home_path - return - end + @attachment.safe_attributes = params[:attachment] + saved = @attachment.save + + respond_to do |format| + format.api { + if saved + render_api_ok + else + render_validation_errors(@attachment) + end + } end - render :action => 'edit' end def destroy @@ -138,6 +154,22 @@ class AttachmentsController < ApplicationController end end + # Returns the menu item that should be selected when viewing an attachment + def current_menu_item + if @attachment + case @attachment.container + when WikiPage + :wiki + when Message + :boards + when Project, Version + :files + else + @attachment.container.class.name.pluralize.downcase.to_sym + end + end + end + private def find_attachment @@ -184,6 +216,10 @@ class AttachmentsController < ApplicationController @attachment.visible? ? true : deny_access end + def update_authorize + @attachment.editable? ? true : deny_access + end + def delete_authorize @attachment.deletable? ? true : deny_access end @@ -203,4 +239,9 @@ class AttachmentsController < ApplicationController 'attachment' end end + + # Returns attachments param for #update_all + def update_all_params + params.permit(:attachments => [:filename, :description]).require(:attachments) + end end diff --git a/app/controllers/auth_sources_controller.rb b/app/controllers/auth_sources_controller.rb index 38bebc439..dcbba3687 100644 --- a/app/controllers/auth_sources_controller.rb +++ b/app/controllers/auth_sources_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,10 +17,12 @@ class AuthSourcesController < ApplicationController layout 'admin' + self.main_menu = false menu_item :ldap_authentication - before_filter :require_admin - before_filter :find_auth_source, :only => [:edit, :update, :test_connection, :destroy] + before_action :require_admin + before_action :build_new_auth_source, :only => [:new, :create] + before_action :find_auth_source, :only => [:edit, :update, :test_connection, :destroy] require_sudo_mode :update, :destroy def index @@ -28,13 +30,9 @@ class AuthSourcesController < ApplicationController end def new - klass_name = params[:type] || 'AuthSourceLdap' - @auth_source = AuthSource.new_subclass_instance(klass_name, params[:auth_source]) - render_404 unless @auth_source end def create - @auth_source = AuthSource.new_subclass_instance(params[:type], params[:auth_source]) if @auth_source.save flash[:notice] = l(:notice_successful_create) redirect_to auth_sources_path @@ -47,7 +45,8 @@ class AuthSourcesController < ApplicationController end def update - if @auth_source.update_attributes(params[:auth_source]) + @auth_source.safe_attributes = params[:auth_source] + if @auth_source.save flash[:notice] = l(:notice_successful_update) redirect_to auth_sources_path else @@ -89,6 +88,15 @@ class AuthSourcesController < ApplicationController private + def build_new_auth_source + @auth_source = AuthSource.new_subclass_instance(params[:type] || 'AuthSourceLdap') + if @auth_source + @auth_source.safe_attributes = params[:auth_source] + else + render_404 + end + end + def find_auth_source @auth_source = AuthSource.find(params[:id]) rescue ActiveRecord::RecordNotFound diff --git a/app/controllers/auto_completes_controller.rb b/app/controllers/auto_completes_controller.rb index 92c0641d6..293ac201e 100644 --- a/app/controllers/auto_completes_controller.rb +++ b/app/controllers/auto_completes_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,17 +16,26 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class AutoCompletesController < ApplicationController - before_filter :find_project + before_action :find_project def issues @issues = [] q = (params[:q] || params[:term]).to_s.strip + status = params[:status].to_s + issue_id = params[:issue_id].to_s if q.present? scope = Issue.cross_project_scope(@project, params[:scope]).visible + if status.present? + scope = scope.open(status == 'o') + end + if issue_id.present? + scope = scope.where.not(:id => issue_id.to_i) + end if q.match(/\A#?(\d+)\z/) @issues << scope.find_by_id($1.to_i) end - @issues += scope.where("LOWER(#{Issue.table_name}.subject) LIKE LOWER(?)", "%#{q}%").order("#{Issue.table_name}.id DESC").limit(10).to_a + + @issues += scope.like(q).order(:id => :desc).limit(10).to_a @issues.compact! end render :layout => false diff --git a/app/controllers/boards_controller.rb b/app/controllers/boards_controller.rb index 9ab80f4f9..03ca50c35 100644 --- a/app/controllers/boards_controller.rb +++ b/app/controllers/boards_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,7 +17,7 @@ class BoardsController < ApplicationController default_search_scope :messages - before_filter :find_project_by_project_id, :find_board_if_available, :authorize + before_action :find_project_by_project_id, :find_board_if_available, :authorize accept_rss_auth :index, :show helper :sort @@ -25,7 +25,7 @@ class BoardsController < ApplicationController helper :watchers def index - @boards = @project.boards.preload(:project, :last_message => :author).to_a + @boards = @project.boards.preload(:last_message => :author).to_a # show the board if there is only one if @boards.size == 1 @board = @boards.first @@ -37,15 +37,14 @@ class BoardsController < ApplicationController respond_to do |format| format.html { sort_init 'updated_on', 'desc' - sort_update 'created_on' => "#{Message.table_name}.created_on", + sort_update 'created_on' => "#{Message.table_name}.id", 'replies' => "#{Message.table_name}.replies_count", - 'updated_on' => "COALESCE(last_replies_messages.created_on, #{Message.table_name}.created_on)" + 'updated_on' => "COALESCE(#{Message.table_name}.last_reply_id, #{Message.table_name}.id)" @topic_count = @board.topics.count @topic_pages = Paginator.new @topic_count, per_page_option, params['page'] @topics = @board.topics. - reorder("#{Message.table_name}.sticky DESC"). - joins("LEFT OUTER JOIN #{Message.table_name} last_replies_messages ON last_replies_messages.id = #{Message.table_name}.last_reply_id"). + reorder(:sticky => :desc). limit(@topic_pages.per_page). offset(@topic_pages.offset). order(sort_clause). @@ -56,7 +55,7 @@ class BoardsController < ApplicationController } format.atom { @messages = @board.messages. - reorder('created_on DESC'). + reorder(:id => :desc). includes(:author, :board). limit(Setting.feeds_limit.to_i). to_a @@ -92,18 +91,20 @@ class BoardsController < ApplicationController flash[:notice] = l(:notice_successful_update) redirect_to_settings_in_projects } - format.js { render :nothing => true } + format.js { head 200 } end else respond_to do |format| format.html { render :action => 'edit' } - format.js { render :nothing => true, :status => 422 } + format.js { head 422 } end end end def destroy - @board.destroy + if @board.destroy + flash[:notice] = l(:notice_successful_delete) + end redirect_to_settings_in_projects end diff --git a/app/controllers/calendars_controller.rb b/app/controllers/calendars_controller.rb index 276d7988d..e07ba2e61 100644 --- a/app/controllers/calendars_controller.rb +++ b/app/controllers/calendars_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,7 +17,7 @@ class CalendarsController < ApplicationController menu_item :calendar - before_filter :find_optional_project + before_action :find_optional_project rescue_from Query::StatementInvalid, :with => :query_statement_invalid @@ -25,8 +25,6 @@ class CalendarsController < ApplicationController helper :projects helper :queries include QueriesHelper - helper :sort - include SortHelper def show if params[:year] and params[:year].to_i > 1900 @@ -41,6 +39,7 @@ class CalendarsController < ApplicationController @calendar = Redmine::Helpers::Calendar.new(Date.civil(@year, @month, 1), current_language, :month) retrieve_query @query.group_by = nil + @query.sort_criteria = nil if @query.valid? events = [] events += @query.issues(:include => [:tracker, :assigned_to, :priority], diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index 5074ef631..e7974d49e 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -18,9 +18,9 @@ class CommentsController < ApplicationController default_search_scope :news model_object News - before_filter :find_model_object - before_filter :find_project_from_association - before_filter :authorize + before_action :find_model_object + before_action :find_project_from_association + before_action :authorize def create raise Unauthorized unless @news.commentable? @@ -43,7 +43,7 @@ class CommentsController < ApplicationController private # ApplicationController's find_model_object sets it based on the controller - # name so it needs to be overriden and set to @news instead + # name so it needs to be overridden and set to @news instead def find_model_object super @news = @object diff --git a/app/controllers/context_menus_controller.rb b/app/controllers/context_menus_controller.rb index 903f13f00..bd6977503 100644 --- a/app/controllers/context_menus_controller.rb +++ b/app/controllers/context_menus_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -19,7 +19,7 @@ class ContextMenusController < ApplicationController helper :watchers helper :issues - before_filter :find_issues, :only => :issues + before_action :find_issues, :only => :issues def issues if (@issues.size == 1) @@ -45,7 +45,7 @@ class ContextMenusController < ApplicationController @options_by_custom_field = {} if @can[:edit] - custom_fields = @issues.map(&:editable_custom_fields).reduce(:&).reject(&:multiple?) + custom_fields = @issues.map(&:editable_custom_fields).reduce(:&).reject(&:multiple?).select {|field| field.format.bulk_edit_supported} custom_fields.each do |field| values = field.possible_values_options(@projects) if values.present? @@ -78,7 +78,7 @@ class ContextMenusController < ApplicationController @options_by_custom_field = {} if @can[:edit] - custom_fields = @time_entries.map(&:editable_custom_fields).reduce(:&).reject(&:multiple?) + custom_fields = @time_entries.map(&:editable_custom_fields).reduce(:&).reject(&:multiple?).select {|field| field.format.bulk_edit_supported} custom_fields.each do |field| values = field.possible_values_options(@projects) if values.present? diff --git a/app/controllers/custom_field_enumerations_controller.rb b/app/controllers/custom_field_enumerations_controller.rb index f99f770f4..fc186b98d 100644 --- a/app/controllers/custom_field_enumerations_controller.rb +++ b/app/controllers/custom_field_enumerations_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,10 +17,11 @@ class CustomFieldEnumerationsController < ApplicationController layout 'admin' + self.main_menu = false - before_filter :require_admin - before_filter :find_custom_field - before_filter :find_enumeration, :only => :destroy + before_action :require_admin + before_action :find_custom_field + before_action :find_enumeration, :only => :destroy helper :custom_fields @@ -29,7 +30,8 @@ class CustomFieldEnumerationsController < ApplicationController end def create - @value = @custom_field.enumerations.build(params[:custom_field_enumeration]) + @value = @custom_field.enumerations.build + @value.attributes = enumeration_params @value.save respond_to do |format| format.html { redirect_to custom_field_enumerations_path(@custom_field) } @@ -38,7 +40,8 @@ class CustomFieldEnumerationsController < ApplicationController end def update_each - if CustomFieldEnumeration.update_each(@custom_field, params[:custom_field_enumerations]) + saved = CustomFieldEnumeration.update_each(@custom_field, update_each_params) + if saved flash[:notice] = l(:notice_successful_update) end redirect_to :action => 'index' @@ -68,4 +71,14 @@ class CustomFieldEnumerationsController < ApplicationController rescue ActiveRecord::RecordNotFound render_404 end + + def enumeration_params + params.require(:custom_field_enumeration).permit(:name, :active, :position) + end + + def update_each_params + # params.require(:custom_field_enumerations).permit(:name, :active, :position) does not work here with param like this: + # "custom_field_enumerations":{"0":{"name": ...}, "1":{"name...}} + params.permit(:custom_field_enumerations => [:name, :active, :position]).require(:custom_field_enumerations) + end end diff --git a/app/controllers/custom_fields_controller.rb b/app/controllers/custom_fields_controller.rb index ab0d5019f..41d4a7301 100644 --- a/app/controllers/custom_fields_controller.rb +++ b/app/controllers/custom_fields_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,16 +17,19 @@ class CustomFieldsController < ApplicationController layout 'admin' + self.main_menu = false - before_filter :require_admin - before_filter :build_new_custom_field, :only => [:new, :create] - before_filter :find_custom_field, :only => [:edit, :update, :destroy] + before_action :require_admin + before_action :build_new_custom_field, :only => [:new, :create] + before_action :find_custom_field, :only => [:edit, :update, :destroy] accept_api_auth :index def index respond_to do |format| format.html { @custom_fields_by_type = CustomField.all.group_by {|f| f.class.name } + @custom_fields_projects_count = + IssueCustomField.where(is_for_all: false).joins(:projects).group(:custom_field_id).count } format.api { @custom_fields = CustomField.all @@ -53,26 +56,29 @@ class CustomFieldsController < ApplicationController end def update - if @custom_field.update_attributes(params[:custom_field]) + @custom_field.safe_attributes = params[:custom_field] + if @custom_field.save call_hook(:controller_custom_fields_edit_after_save, :params => params, :custom_field => @custom_field) respond_to do |format| format.html { flash[:notice] = l(:notice_successful_update) redirect_back_or_default edit_custom_field_path(@custom_field) } - format.js { render :nothing => true } + format.js { head 200 } end else respond_to do |format| format.html { render :action => 'edit' } - format.js { render :nothing => true, :status => 422 } + format.js { head 422 } end end end def destroy begin - @custom_field.destroy + if @custom_field.destroy + flash[:notice] = l(:notice_successful_delete) + end rescue flash[:error] = l(:error_can_not_delete_custom_field) end @@ -82,9 +88,11 @@ class CustomFieldsController < ApplicationController private def build_new_custom_field - @custom_field = CustomField.new_subclass_instance(params[:type], params[:custom_field]) + @custom_field = CustomField.new_subclass_instance(params[:type]) if @custom_field.nil? render :action => 'select_type' + else + @custom_field.safe_attributes = params[:custom_field] end end diff --git a/app/controllers/documents_controller.rb b/app/controllers/documents_controller.rb index 40db8af1e..dbf0e88dc 100644 --- a/app/controllers/documents_controller.rb +++ b/app/controllers/documents_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -18,10 +18,10 @@ class DocumentsController < ApplicationController default_search_scope :documents model_object Document - before_filter :find_project_by_project_id, :only => [:index, :new, :create] - before_filter :find_model_object, :except => [:index, :new, :create] - before_filter :find_project_from_association, :except => [:index, :new, :create] - before_filter :authorize + before_action :find_project_by_project_id, :only => [:index, :new, :create] + before_action :find_model_object, :except => [:index, :new, :create] + before_action :find_project_from_association, :except => [:index, :new, :create] + before_action :authorize helper :attachments helper :custom_fields diff --git a/app/controllers/email_addresses_controller.rb b/app/controllers/email_addresses_controller.rb index dfc80ad95..22026254a 100644 --- a/app/controllers/email_addresses_controller.rb +++ b/app/controllers/email_addresses_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,8 +16,9 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class EmailAddressesController < ApplicationController - before_filter :find_user, :require_admin_or_current_user - before_filter :find_email_address, :only => [:update, :destroy] + self.main_menu = false + before_action :find_user, :require_admin_or_current_user + before_action :find_email_address, :only => [:update, :destroy] require_sudo_mode :create, :update, :destroy def index @@ -29,10 +30,7 @@ class EmailAddressesController < ApplicationController saved = false if @user.email_addresses.count <= Setting.max_additional_emails.to_i @address = EmailAddress.new(:user => @user, :is_default => false) - attrs = params[:email_address] - if attrs.is_a?(Hash) - @address.address = attrs[:address].to_s - end + @address.safe_attributes = params[:email_address] saved = @address.save end diff --git a/app/controllers/enumerations_controller.rb b/app/controllers/enumerations_controller.rb index feb360398..a04d7b184 100644 --- a/app/controllers/enumerations_controller.rb +++ b/app/controllers/enumerations_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,11 +17,12 @@ class EnumerationsController < ApplicationController layout 'admin' + self.main_menu = false - before_filter :require_admin, :except => :index - before_filter :require_admin_or_api_request, :only => :index - before_filter :build_new_enumeration, :only => [:new, :create] - before_filter :find_enumeration, :only => [:edit, :update, :destroy] + before_action :require_admin, :except => :index + before_action :require_admin_or_api_request, :only => :index + before_action :build_new_enumeration, :only => [:new, :create] + before_action :find_enumeration, :only => [:edit, :update, :destroy] accept_api_auth :index helper :custom_fields @@ -56,18 +57,18 @@ class EnumerationsController < ApplicationController end def update - if @enumeration.update_attributes(params[:enumeration]) + if @enumeration.update_attributes(enumeration_params) respond_to do |format| format.html { flash[:notice] = l(:notice_successful_update) redirect_to enumerations_path } - format.js { render :nothing => true } + format.js { head 200 } end else respond_to do |format| format.html { render :action => 'edit' } - format.js { render :nothing => true, :status => 422 } + format.js { head 422 } end end end @@ -90,8 +91,10 @@ class EnumerationsController < ApplicationController def build_new_enumeration class_name = params[:enumeration] && params[:enumeration][:type] || params[:type] - @enumeration = Enumeration.new_subclass_instance(class_name, params[:enumeration]) - if @enumeration.nil? + @enumeration = Enumeration.new_subclass_instance(class_name) + if @enumeration + @enumeration.attributes = enumeration_params || {} + else render_404 end end @@ -101,4 +104,10 @@ class EnumerationsController < ApplicationController rescue ActiveRecord::RecordNotFound render_404 end + + def enumeration_params + # can't require enumeration on #new action + 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 diff --git a/app/controllers/files_controller.rb b/app/controllers/files_controller.rb index db061b858..7c596657c 100644 --- a/app/controllers/files_controller.rb +++ b/app/controllers/files_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -18,9 +18,11 @@ class FilesController < ApplicationController menu_item :files - before_filter :find_project_by_project_id - before_filter :authorize + before_action :find_project_by_project_id + before_action :authorize + accept_api_auth :index, :create + helper :attachments helper :sort include SortHelper @@ -35,7 +37,10 @@ class FilesController < ApplicationController references(:attachments).reorder(sort_clause).find(@project.id)] @containers += @project.versions.includes(:attachments). references(:attachments).reorder(sort_clause).to_a.sort.reverse - render :layout => !request.xhr? + respond_to do |format| + format.html { render :layout => !request.xhr? } + format.api + end end def new @@ -43,20 +48,29 @@ class FilesController < ApplicationController end def create - container = (params[:version_id].blank? ? @project : @project.versions.find_by_id(params[:version_id])) - attachments = Attachment.attach_files(container, params[:attachments]) + version_id = params[:version_id] || (params[:file] && params[:file][:version_id]) + container = version_id.blank? ? @project : @project.versions.find_by_id(version_id) + attachments = Attachment.attach_files(container, (params[:attachments] || (params[:file] && params[:file][:token] && params))) render_attachment_warning_if_needed(container) if attachments[:files].present? if Setting.notified_events.include?('file_added') Mailer.attachments_added(attachments[:files]).deliver end - flash[:notice] = l(:label_file_added) - redirect_to project_files_path(@project) + respond_to do |format| + format.html { + flash[:notice] = l(:label_file_added) + redirect_to project_files_path(@project) } + format.api { render_api_ok } + end else - flash.now[:error] = l(:label_attachment) + " " + l('activerecord.errors.messages.invalid') - new - render :action => 'new' + respond_to do |format| + format.html { + flash.now[:error] = l(:label_attachment) + " " + l('activerecord.errors.messages.invalid') + new + render :action => 'new' } + format.api { render :status => :bad_request } + end end end end diff --git a/app/controllers/gantts_controller.rb b/app/controllers/gantts_controller.rb index e37d780dd..3283282d5 100644 --- a/app/controllers/gantts_controller.rb +++ b/app/controllers/gantts_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,7 +17,7 @@ class GanttsController < ApplicationController menu_item :gantt - before_filter :find_optional_project + before_action :find_optional_project rescue_from Query::StatementInvalid, :with => :query_statement_invalid @@ -26,8 +26,6 @@ class GanttsController < ApplicationController helper :projects helper :queries include QueriesHelper - helper :sort - include SortHelper include Redmine::Export::PDF def show diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index b99e374ff..4379ee39b 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,9 +17,10 @@ class GroupsController < ApplicationController layout 'admin' + self.main_menu = false - before_filter :require_admin - before_filter :find_group, :except => [:index, :new, :create] + before_action :require_admin + before_action :find_group, :except => [:index, :new, :create] accept_api_auth :index, :show, :create, :update, :destroy, :add_users, :remove_user require_sudo_mode :add_users, :remove_user, :create, :update, :destroy, :edit_membership, :destroy_membership @@ -30,7 +31,12 @@ class GroupsController < ApplicationController def index respond_to do |format| format.html { - @groups = Group.sorted.to_a + scope = Group.sorted + scope = scope.like(params[:name]) if params[:name].present? + + @group_count = scope.count + @group_pages = Paginator.new @group_count, per_page_option, params['page'] + @groups = scope.limit(@group_pages.per_page).offset(@group_pages.offset).to_a @user_count_by_group_id = user_count_by_group_id } format.api { @@ -79,7 +85,7 @@ class GroupsController < ApplicationController respond_to do |format| if @group.save flash[:notice] = l(:notice_successful_update) - format.html { redirect_to(groups_path) } + format.html { redirect_to_referer_or(groups_path) } format.api { render_api_ok } else format.html { render :action => "edit" } @@ -92,7 +98,7 @@ class GroupsController < ApplicationController @group.destroy respond_to do |format| - format.html { redirect_to(groups_path) } + format.html { redirect_to_referer_or(groups_path) } format.api { render_api_ok } end end diff --git a/app/controllers/imports_controller.rb b/app/controllers/imports_controller.rb index 079bce347..96589ac65 100644 --- a/app/controllers/imports_controller.rb +++ b/app/controllers/imports_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -18,11 +18,13 @@ require 'csv' class ImportsController < ApplicationController + menu_item :issues - before_filter :find_import, :only => [:show, :settings, :mapping, :run] - before_filter :authorize_global + before_action :find_import, :only => [:show, :settings, :mapping, :run] + before_action :authorize_global helper :issues + helper :queries def new end diff --git a/app/controllers/issue_categories_controller.rb b/app/controllers/issue_categories_controller.rb index c53f2395d..d635a28f3 100644 --- a/app/controllers/issue_categories_controller.rb +++ b/app/controllers/issue_categories_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -18,10 +18,10 @@ class IssueCategoriesController < ApplicationController menu_item :settings model_object IssueCategory - before_filter :find_model_object, :except => [:index, :new, :create] - before_filter :find_project_from_association, :except => [:index, :new, :create] - before_filter :find_project_by_project_id, :only => [:index, :new, :create] - before_filter :authorize + before_action :find_model_object, :except => [:index, :new, :create] + before_action :find_project_from_association, :except => [:index, :new, :create] + before_action :find_project_by_project_id, :only => [:index, :new, :create] + before_action :authorize accept_api_auth :index, :show, :create, :update, :destroy def index diff --git a/app/controllers/issue_relations_controller.rb b/app/controllers/issue_relations_controller.rb index 1258928c8..0bcc8c5e3 100644 --- a/app/controllers/issue_relations_controller.rb +++ b/app/controllers/issue_relations_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,8 +16,10 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class IssueRelationsController < ApplicationController - before_filter :find_issue, :authorize, :only => [:index, :create] - before_filter :find_relation, :only => [:show, :destroy] + helper :issues + + before_action :find_issue, :authorize, :only => [:index, :create] + before_action :find_relation, :only => [:show, :destroy] accept_api_auth :index, :show, :create, :destroy @@ -25,7 +27,7 @@ class IssueRelationsController < ApplicationController @relations = @issue.relations respond_to do |format| - format.html { render :nothing => true } + format.html { head 200 } format.api end end @@ -34,7 +36,7 @@ class IssueRelationsController < ApplicationController raise Unauthorized unless @relation.visible? respond_to do |format| - format.html { render :nothing => true } + format.html { head 200 } format.api end end diff --git a/app/controllers/issue_statuses_controller.rb b/app/controllers/issue_statuses_controller.rb index e49878f1d..92c033783 100644 --- a/app/controllers/issue_statuses_controller.rb +++ b/app/controllers/issue_statuses_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,9 +17,10 @@ class IssueStatusesController < ApplicationController layout 'admin' + self.main_menu = false - before_filter :require_admin, :except => :index - before_filter :require_admin_or_api_request, :only => :index + before_action :require_admin, :except => :index + before_action :require_admin_or_api_request, :only => :index accept_api_auth :index def index @@ -35,7 +36,8 @@ class IssueStatusesController < ApplicationController end def create - @issue_status = IssueStatus.new(params[:issue_status]) + @issue_status = IssueStatus.new + @issue_status.safe_attributes = params[:issue_status] if @issue_status.save flash[:notice] = l(:notice_successful_create) redirect_to issue_statuses_path @@ -50,18 +52,19 @@ class IssueStatusesController < ApplicationController def update @issue_status = IssueStatus.find(params[:id]) - if @issue_status.update_attributes(params[:issue_status]) + @issue_status.safe_attributes = params[:issue_status] + if @issue_status.save respond_to do |format| format.html { flash[:notice] = l(:notice_successful_update) redirect_to issue_statuses_path(:page => params[:page]) } - format.js { render :nothing => true } + format.js { head 200 } end else respond_to do |format| format.html { render :action => 'edit' } - format.js { render :nothing => true, :status => 422 } + format.js { head 422 } end end end diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index 91c196b58..69a947b03 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -18,11 +18,11 @@ class IssuesController < ApplicationController default_search_scope :issues - before_filter :find_issue, :only => [:show, :edit, :update] - before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :destroy] - before_filter :authorize, :except => [:index, :new, :create] - before_filter :find_optional_project, :only => [:index, :new, :create] - before_filter :build_new_issue_from_params, :only => [:new, :create] + before_action :find_issue, :only => [:show, :edit, :update] + before_action :find_issues, :only => [:bulk_edit, :bulk_update, :destroy] + before_action :authorize, :except => [:index, :new, :create] + before_action :find_optional_project, :only => [:index, :new, :create] + before_action :build_new_issue_from_params, :only => [:new, :create] accept_rss_auth :index, :show accept_api_auth :index, :show, :create, :update, :destroy @@ -37,54 +37,44 @@ class IssuesController < ApplicationController helper :queries include QueriesHelper helper :repositories - helper :sort - include SortHelper helper :timelog def index - retrieve_query - sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria) - sort_update(@query.sortable_columns) - @query.sort_criteria = sort_criteria.to_a + use_session = !request.format.csv? + retrieve_query(IssueQuery, use_session) if @query.valid? - case params[:format] - when 'csv', 'pdf' - @limit = Setting.issues_export_limit.to_i - if params[:columns] == 'all' - @query.column_names = @query.available_inline_columns.map(&:name) - end - when 'atom' - @limit = Setting.feeds_limit.to_i - when 'xml', 'json' - @offset, @limit = api_offset_and_limit - @query.column_names = %w(author) - else - @limit = per_page_option - end - - @issue_count = @query.issue_count - @issue_pages = Paginator.new @issue_count, @limit, params['page'] - @offset ||= @issue_pages.offset - @issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version], - :order => sort_clause, - :offset => @offset, - :limit => @limit) - @issue_count_by_group = @query.issue_count_by_group - respond_to do |format| - format.html { render :template => 'issues/index', :layout => !request.xhr? } + format.html { + @issue_count = @query.issue_count + @issue_pages = Paginator.new @issue_count, per_page_option, params['page'] + @issues = @query.issues(:offset => @issue_pages.offset, :limit => @issue_pages.per_page) + render :layout => !request.xhr? + } format.api { + @offset, @limit = api_offset_and_limit + @query.column_names = %w(author) + @issue_count = @query.issue_count + @issues = @query.issues(:offset => @offset, :limit => @limit) Issue.load_visible_relations(@issues) if include_in_api_response?('relations') } - format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") } - format.csv { send_data(query_to_csv(@issues, @query, params[:csv]), :type => 'text/csv; header=present', :filename => 'issues.csv') } - format.pdf { send_file_headers! :type => 'application/pdf', :filename => 'issues.pdf' } + format.atom { + @issues = @query.issues(:limit => Setting.feeds_limit.to_i) + render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") + } + format.csv { + @issues = @query.issues(:limit => Setting.issues_export_limit.to_i) + send_data(query_to_csv(@issues, @query, params[:csv]), :type => 'text/csv; header=present', :filename => 'issues.csv') + } + format.pdf { + @issues = @query.issues(:limit => Setting.issues_export_limit.to_i) + send_file_headers! :type => 'application/pdf', :filename => 'issues.pdf' + } end else respond_to do |format| - format.html { render(:template => 'issues/index', :layout => !request.xhr?) } - format.any(:atom, :csv, :pdf) { render(:nothing => true) } + format.html { render :layout => !request.xhr? } + format.any(:atom, :csv, :pdf) { head 422 } format.api { render_validation_errors(@query) } end end @@ -93,23 +83,14 @@ class IssuesController < ApplicationController end def show - @journals = @issue.journals.includes(:user, :details). - references(:user, :details). - reorder(:created_on, :id).to_a - @journals.each_with_index {|j,i| j.indice = i+1} - @journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project) - Journal.preload_journals_details_custom_fields(@journals) - @journals.select! {|journal| journal.notes? || journal.visible_details.any?} - @journals.reverse! if User.current.wants_comments_in_reverse_order? - + @journals = @issue.visible_journals_with_index @changesets = @issue.changesets.visible.preload(:repository, :user).to_a - @changesets.reverse! if User.current.wants_comments_in_reverse_order? - @relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? } - @allowed_statuses = @issue.new_statuses_allowed_to(User.current) - @priorities = IssuePriority.active - @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project) - @relation = IssueRelation.new + + if User.current.wants_comments_in_reverse_order? + @journals.reverse! + @changesets.reverse! + end if User.current.allowed_to?(:view_time_entries, @project) Issue.load_visible_spent_hours([@issue]) @@ -118,6 +99,10 @@ class IssuesController < ApplicationController respond_to do |format| format.html { + @allowed_statuses = @issue.new_statuses_allowed_to(User.current) + @priorities = IssuePriority.active + @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project) + @relation = IssueRelation.new retrieve_previous_and_next_issue_ids render :template => 'issues/show' } @@ -222,32 +207,69 @@ class IssuesController < ApplicationController end end + edited_issues = Issue.where(:id => @issues.map(&:id)).to_a + + @values_by_custom_field = {} + edited_issues.each do |issue| + issue.custom_field_values.each do |c| + if c.value_present? + @values_by_custom_field[c.custom_field] ||= [] + @values_by_custom_field[c.custom_field] << issue.id + end + end + end + @allowed_projects = Issue.allowed_target_projects if params[:issue] @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:issue][:project_id].to_s} if @target_project target_projects = [@target_project] + edited_issues.each {|issue| issue.project = @target_project} end end target_projects ||= @projects + @trackers = target_projects.map {|p| Issue.allowed_target_trackers(p) }.reduce(:&) + if params[:issue] + @target_tracker = @trackers.detect {|t| t.id.to_s == params[:issue][:tracker_id].to_s} + if @target_tracker + edited_issues.each {|issue| issue.tracker = @target_tracker} + end + end + if @copy # Copied issues will get their default statuses @available_statuses = [] else - @available_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&) + @available_statuses = edited_issues.map(&:new_statuses_allowed_to).reduce(:&) end - @custom_fields = @issues.map{|i|i.editable_custom_fields}.reduce(:&) + if params[:issue] + @target_status = @available_statuses.detect {|t| t.id.to_s == params[:issue][:status_id].to_s} + if @target_status + edited_issues.each {|issue| issue.status = @target_status} + end + end + + edited_issues.each do |issue| + issue.custom_field_values.each do |c| + if c.value_present? && @values_by_custom_field[c.custom_field] + @values_by_custom_field[c.custom_field].delete(issue.id) + end + end + end + @values_by_custom_field.delete_if {|k,v| v.blank?} + + @custom_fields = edited_issues.map{|i|i.editable_custom_fields}.reduce(:&).select {|field| field.format.bulk_edit_supported} @assignables = target_projects.map(&:assignable_users).reduce(:&) - @trackers = target_projects.map {|p| Issue.allowed_target_trackers(p) }.reduce(:&) @versions = target_projects.map {|p| p.shared_versions.open}.reduce(:&) @categories = target_projects.map {|p| p.issue_categories}.reduce(:&) if @copy @attachments_present = @issues.detect {|i| i.attachments.any?}.present? @subtasks_present = @issues.detect {|i| !i.leaf?}.present? + @watchers_present = User.current.allowed_to?(:add_issue_watchers, @projects) && Watcher.where(:watchable_type => 'Issue', :watchable_id => @issues.map(&:id)).exists? end - @safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&) + @safe_attributes = edited_issues.map(&:safe_attribute_names).reduce(:&) @issue_params = params[:issue] || {} @issue_params[:custom_field_values] ||= {} @@ -260,6 +282,7 @@ class IssuesController < ApplicationController attributes = parse_params_for_bulk_update(params[:issue]) copy_subtasks = (params[:copy_subtasks] == '1') copy_attachments = (params[:copy_attachments] == '1') + copy_watchers = (params[:copy_watchers] == '1') if @copy unless User.current.allowed_to?(:copy_issues, @projects) @@ -272,6 +295,9 @@ class IssuesController < ApplicationController unless User.current.allowed_to?(:add_issues, target_projects) raise ::Unauthorized end + unless User.current.allowed_to?(:add_issue_watchers, @projects) + copy_watchers = false + end else unless @issues.all?(&:attributes_editable?) raise ::Unauthorized @@ -293,6 +319,7 @@ class IssuesController < ApplicationController issue = orig_issue.copy({}, :attachments => copy_attachments, :subtasks => copy_subtasks, + :watchers => copy_watchers, :link => link_copy?(params[:link_copy]) ) else @@ -341,7 +368,12 @@ class IssuesController < ApplicationController when 'destroy' # nothing to do 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) + end when 'reassign' reassign_to = @project && @project.issues.find_by_id(params[:reassign_to_id]) if reassign_to.nil? @@ -374,7 +406,7 @@ class IssuesController < ApplicationController # Overrides Redmine::MenuManager::MenuController::ClassMethods for # when the "New issue" tab is enabled def current_menu_item - if Setting.new_item_menu_tab == '1' && [:new, :create].include?(action_name.to_sym) + if Setting.new_item_menu_tab == '1' && [:new, :create].include?(action_name.to_sym) :new_issue else super @@ -392,10 +424,9 @@ class IssuesController < ApplicationController else retrieve_query_from_session if @query - sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria) - sort_update(@query.sortable_columns, 'issues_index_sort') + @per_page = per_page_option limit = 500 - issue_ids = @query.issue_ids(:order => sort_clause, :limit => (limit + 1), :include => [:assigned_to, :tracker, :priority, :category, :fixed_version]) + issue_ids = @query.issue_ids(:limit => (limit + 1)) if (idx = issue_ids.index(@issue.id)) && idx < limit if issue_ids.size < 500 @issue_position = idx + 1 @@ -404,6 +435,11 @@ class IssuesController < ApplicationController @prev_issue_id = issue_ids[idx - 1] if idx > 0 @next_issue_id = issue_ids[idx + 1] if idx < (issue_ids.size - 1) end + query_params = @query.as_params + if @issue_position + query_params = query_params.merge(:page => (@issue_position / per_page_option) + 1, :per_page => per_page_option) + end + @query_path = _project_issues_path(@query.project, query_params) end end end @@ -460,7 +496,8 @@ class IssuesController < ApplicationController @link_copy = link_copy?(params[:link_copy]) || request.get? @copy_attachments = params[:copy_attachments].present? || request.get? @copy_subtasks = params[:copy_subtasks].present? || request.get? - @issue.copy_from(@copy_from, :attachments => @copy_attachments, :subtasks => @copy_subtasks, :link => @link_copy) + @copy_watchers = User.current.allowed_to?(:add_issue_watchers, @project) + @issue.copy_from(@copy_from, :attachments => @copy_attachments, :subtasks => @copy_subtasks, :watchers => @copy_watchers, :link => @link_copy) @issue.parent_issue_id = @copy_from.parent_id rescue ActiveRecord::RecordNotFound render_404 @@ -519,7 +556,7 @@ class IssuesController < ApplicationController time_entry.issue = @issue time_entry.user = User.current time_entry.spent_on = User.current.today - time_entry.attributes = params[:time_entry] + time_entry.safe_attributes = params[:time_entry] @issue.time_entries << time_entry end @@ -548,15 +585,18 @@ class IssuesController < ApplicationController # Redirects user after a successful issue creation def redirect_after_create if params[:continue] - attrs = {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} + url_params = {} + url_params[:issue] = {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} + url_params[:back_url] = params[:back_url].presence + if params[:project_id] - redirect_to new_project_issue_path(@issue.project, :issue => attrs) + redirect_to new_project_issue_path(@issue.project, url_params) else - attrs.merge! :project_id => @issue.project_id - redirect_to new_issue_path(:issue => attrs) + url_params[:issue].merge! :project_id => @issue.project_id + redirect_to new_issue_path(url_params) end else - redirect_to issue_path(@issue) + redirect_back_or_default issue_path(@issue) end end end diff --git a/app/controllers/journals_controller.rb b/app/controllers/journals_controller.rb index a0905aed1..7f07b38a8 100644 --- a/app/controllers/journals_controller.rb +++ b/app/controllers/journals_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,10 +16,10 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class JournalsController < ApplicationController - before_filter :find_journal, :only => [:edit, :update, :diff] - before_filter :find_issue, :only => [:new] - before_filter :find_optional_project, :only => [:index] - before_filter :authorize, :only => [:new, :edit, :update, :diff] + before_action :find_journal, :only => [:edit, :update, :diff] + before_action :find_issue, :only => [:new] + before_action :find_optional_project, :only => [:index] + before_action :authorize, :only => [:new, :edit, :update, :diff] accept_rss_auth :index menu_item :issues @@ -27,13 +27,9 @@ class JournalsController < ApplicationController helper :custom_fields helper :queries include QueriesHelper - helper :sort - include SortHelper def index retrieve_query - sort_init 'id', 'desc' - sort_update(@query.sortable_columns) if @query.valid? @journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC", :limit => 25) @@ -90,7 +86,8 @@ class JournalsController < ApplicationController def update (render_403; return false) unless @journal.editable_by?(User.current) - @journal.update_attributes(:notes => params[:notes]) if params[:notes] + @journal.safe_attributes = params[:journal] + @journal.save @journal.destroy if @journal.details.empty? && @journal.notes.blank? call_hook(:controller_journals_edit_post, { :journal => @journal, :params => params}) respond_to do |format| diff --git a/app/controllers/mail_handler_controller.rb b/app/controllers/mail_handler_controller.rb index ca08a2449..ccb832e7a 100644 --- a/app/controllers/mail_handler_controller.rb +++ b/app/controllers/mail_handler_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,7 +16,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class MailHandlerController < ActionController::Base - before_filter :check_credential + before_action :check_credential # Displays the email submission form def new @@ -27,9 +27,9 @@ class MailHandlerController < ActionController::Base options = params.dup email = options.delete(:email) if MailHandler.receive(email, options) - render :nothing => true, :status => :created + head :created else - render :nothing => true, :status => :unprocessable_entity + head :unprocessable_entity end end @@ -38,7 +38,7 @@ class MailHandlerController < ActionController::Base def check_credential User.current = nil unless Setting.mail_handler_api_enabled? && params[:key].to_s == Setting.mail_handler_api_key - render :text => 'Access denied. Incoming emails WS is disabled or key is invalid.', :status => 403 + render :plain => 'Access denied. Incoming emails WS is disabled or key is invalid.', :status => 403 end end end diff --git a/app/controllers/members_controller.rb b/app/controllers/members_controller.rb index 173b3ee66..1583e9e78 100644 --- a/app/controllers/members_controller.rb +++ b/app/controllers/members_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,10 +17,10 @@ class MembersController < ApplicationController model_object Member - before_filter :find_model_object, :except => [:index, :new, :create, :autocomplete] - before_filter :find_project_from_association, :except => [:index, :new, :create, :autocomplete] - before_filter :find_project_by_project_id, :only => [:index, :new, :create, :autocomplete] - before_filter :authorize + before_action :find_model_object, :except => [:index, :new, :create, :autocomplete] + before_action :find_project_from_association, :except => [:index, :new, :create, :autocomplete] + before_action :find_project_by_project_id, :only => [:index, :new, :create, :autocomplete] + before_action :authorize accept_api_auth :index, :show, :create, :update, :destroy require_sudo_mode :create, :update, :destroy @@ -80,6 +80,10 @@ class MembersController < ApplicationController end end + def edit + @roles = Role.givable.to_a + end + def update if params[:membership] @member.set_editable_role_ids(params[:membership][:role_ids]) diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb index 487dcbe07..76bc19cf6 100644 --- a/app/controllers/messages_controller.rb +++ b/app/controllers/messages_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -18,10 +18,10 @@ class MessagesController < ApplicationController menu_item :boards default_search_scope :messages - before_filter :find_board, :only => [:new, :preview] - before_filter :find_attachments, :only => [:preview] - before_filter :find_message, :except => [:new, :preview] - before_filter :authorize, :except => [:preview, :edit, :destroy] + before_action :find_board, :only => [:new, :preview] + before_action :find_attachments, :only => [:preview] + before_action :find_message, :except => [:new, :preview] + before_action :authorize, :except => [:preview, :edit, :destroy] helper :boards helper :watchers diff --git a/app/controllers/my_controller.rb b/app/controllers/my_controller.rb index 15ec0892a..bf04d55af 100644 --- a/app/controllers/my_controller.rb +++ b/app/controllers/my_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,9 +16,10 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class MyController < ApplicationController - before_filter :require_login + self.main_menu = false + before_action :require_login # let user change user's password when user has to - skip_before_filter :check_password_change, :only => :password + skip_before_action :check_password_change, :only => :password require_sudo_mode :account, only: :post require_sudo_mode :reset_rss_key, :reset_api_key, :show_api_key, :destroy @@ -26,19 +27,7 @@ class MyController < ApplicationController helper :issues helper :users helper :custom_fields - - BLOCKS = { 'issuesassignedtome' => :label_assigned_to_me_issues, - 'issuesreportedbyme' => :label_reported_issues, - 'issueswatched' => :label_watched_issues, - 'news' => :label_news_latest, - 'calendar' => :label_calendar, - 'documents' => :label_document_plural, - 'timelog' => :label_spent_time - }.merge(Redmine::Views::MyPage::Block.additional_blocks).freeze - - DEFAULT_LAYOUT = { 'left' => ['issuesassignedtome'], - 'right' => ['issuesreportedbyme'] - }.freeze + helper :queries def index page @@ -48,7 +37,8 @@ class MyController < ApplicationController # Show user's page def page @user = User.current - @blocks = @user.pref[:my_page_layout] || DEFAULT_LAYOUT + @groups = @user.pref.my_page_groups + @blocks = @user.pref.my_page_layout end # Edit user's account @@ -56,8 +46,8 @@ class MyController < ApplicationController @user = User.current @pref = @user.pref if request.post? - @user.safe_attributes = params[:user] if params[:user] - @user.pref.attributes = params[:pref] if params[:pref] + @user.safe_attributes = params[:user] + @user.pref.safe_attributes = params[:pref] if @user.save @user.pref.save set_language_if_valid @user.language @@ -143,69 +133,54 @@ class MyController < ApplicationController redirect_to my_account_path end - # User's page layout configuration - def page_layout + def update_page @user = User.current - @blocks = @user.pref[:my_page_layout] || DEFAULT_LAYOUT.dup - @block_options = [] - BLOCKS.each do |k, v| - unless @blocks.values.flatten.include?(k) - @block_options << [l("my.blocks.#{v}", :default => [v, v.to_s.humanize]), k.dasherize] - end + block_settings = params[:settings] || {} + + block_settings.each do |block, settings| + @user.pref.update_block_settings(block, settings) end + @user.pref.save + @updated_blocks = block_settings.keys end # Add a block to user's page # The block is added on top of the page # params[:block] : id of the block to add def add_block - block = params[:block].to_s.underscore - if block.present? && BLOCKS.key?(block) - @user = User.current - layout = @user.pref[:my_page_layout] || {} - # remove if already present in a group - %w(top left right).each {|f| (layout[f] ||= []).delete block } - # add it on top - layout['top'].unshift block - @user.pref[:my_page_layout] = layout + @user = User.current + @block = params[:block] + if @user.pref.add_block @block @user.pref.save + respond_to do |format| + format.html { redirect_to my_page_path } + format.js + end + else + render_error :status => 422 end - redirect_to my_page_layout_path end # Remove a block to user's page # params[:block] : id of the block to remove def remove_block - block = params[:block].to_s.underscore @user = User.current - # remove block in all groups - layout = @user.pref[:my_page_layout] || {} - %w(top left right).each {|f| (layout[f] ||= []).delete block } - @user.pref[:my_page_layout] = layout + @block = params[:block] + @user.pref.remove_block @block @user.pref.save - redirect_to my_page_layout_path + respond_to do |format| + format.html { redirect_to my_page_path } + format.js + end end # Change blocks order on user's page # params[:group] : group to order (top, left or right) - # params[:list-(top|left|right)] : array of block ids of the group + # params[:blocks] : array of block ids of the group def order_blocks - group = params[:group] @user = User.current - if group.is_a?(String) - group_items = (params["blocks"] || []).collect(&:underscore) - group_items.each {|s| s.sub!(/^block_/, '')} - if group_items and group_items.is_a? Array - layout = @user.pref[:my_page_layout] || {} - # remove group blocks if they are presents in other groups - %w(top left right).each {|f| - layout[f] = (layout[f] || []) - group_items - } - layout[group] = group_items - @user.pref[:my_page_layout] = layout - @user.pref.save - end - end - render :nothing => true + @user.pref.order_blocks params[:group], params[:blocks] + @user.pref.save + head 200 end end diff --git a/app/controllers/news_controller.rb b/app/controllers/news_controller.rb index 6e429e827..3df9e5e44 100644 --- a/app/controllers/news_controller.rb +++ b/app/controllers/news_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -18,11 +18,11 @@ class NewsController < ApplicationController default_search_scope :news model_object News - before_filter :find_model_object, :except => [:new, :create, :index] - before_filter :find_project_from_association, :except => [:new, :create, :index] - before_filter :find_project_by_project_id, :only => [:new, :create] - before_filter :authorize, :except => [:index] - before_filter :find_optional_project, :only => :index + before_action :find_model_object, :except => [:new, :create, :index] + before_action :find_project_from_association, :except => [:new, :create, :index] + before_action :find_project_by_project_id, :only => [:new, :create] + before_action :authorize, :except => [:index] + before_action :find_optional_project, :only => :index accept_rss_auth :index accept_api_auth :index @@ -98,14 +98,4 @@ class NewsController < ApplicationController @news.destroy redirect_to project_news_index_path(@project) end - - private - - def find_optional_project - return true unless params[:project_id] - @project = Project.find(params[:project_id]) - authorize - rescue ActiveRecord::RecordNotFound - render_404 - end end diff --git a/app/controllers/previews_controller.rb b/app/controllers/previews_controller.rb index 48828b199..37cdc4668 100644 --- a/app/controllers/previews_controller.rb +++ b/app/controllers/previews_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,7 +16,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class PreviewsController < ApplicationController - before_filter :find_project, :find_attachments + before_action :find_project, :find_attachments def issue @issue = Issue.visible.find_by_id(params[:id]) unless params[:id].blank? @@ -25,8 +25,8 @@ class PreviewsController < ApplicationController if @description && @description.gsub(/(\r?\n|\n\r?)/, "\n") == @issue.description.to_s.gsub(/(\r?\n|\n\r?)/, "\n") @description = nil end - # params[:notes] is useful for preview of notes in issue history - @notes = params[:notes] || (params[:issue] ? params[:issue][:notes] : nil) + @notes = params[:journal] ? params[:journal][:notes] : nil + @notes ||= params[:issue] ? params[:issue][:notes] : nil else @description = (params[:issue] ? params[:issue][:description] : nil) end diff --git a/app/controllers/principal_memberships_controller.rb b/app/controllers/principal_memberships_controller.rb index 5cf8ff166..924ecbdf9 100644 --- a/app/controllers/principal_memberships_controller.rb +++ b/app/controllers/principal_memberships_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,10 +17,11 @@ class PrincipalMembershipsController < ApplicationController layout 'admin' + self.main_menu = false - before_filter :require_admin - before_filter :find_principal, :only => [:new, :create] - before_filter :find_membership, :only => [:update, :destroy] + before_action :require_admin + before_action :find_principal, :only => [:new, :create] + before_action :find_membership, :only => [:edit, :update, :destroy] def new @projects = Project.active.all @@ -39,8 +40,12 @@ class PrincipalMembershipsController < ApplicationController end end + def edit + @roles = Role.givable.to_a + end + def update - @membership.attributes = params[:membership] + @membership.attributes = params.require(:membership).permit(:role_ids => []) @membership.save respond_to do |format| format.html { redirect_to_principal @principal } diff --git a/app/controllers/project_enumerations_controller.rb b/app/controllers/project_enumerations_controller.rb index df3d8c2c4..f68d94869 100644 --- a/app/controllers/project_enumerations_controller.rb +++ b/app/controllers/project_enumerations_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,8 +16,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class ProjectEnumerationsController < ApplicationController - before_filter :find_project_by_project_id - before_filter :authorize + before_action :find_project_by_project_id + before_action :authorize def update if params[:enumerations] diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 8753badde..3e3fc69f3 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -18,21 +18,16 @@ class ProjectsController < ApplicationController menu_item :overview menu_item :settings, :only => :settings + menu_item :projects, :only => [:index, :new, :copy, :create] - before_filter :find_project, :except => [ :index, :list, :new, :create, :copy ] - before_filter :authorize, :except => [ :index, :list, :new, :create, :copy, :archive, :unarchive, :destroy] - before_filter :authorize_global, :only => [:new, :create] - before_filter :require_admin, :only => [ :copy, :archive, :unarchive, :destroy ] + before_action :find_project, :except => [ :index, :autocomplete, :list, :new, :create, :copy ] + before_action :authorize, :except => [ :index, :autocomplete, :list, :new, :create, :copy, :archive, :unarchive, :destroy] + before_action :authorize_global, :only => [:new, :create] + before_action :require_admin, :only => [ :copy, :archive, :unarchive, :destroy ] accept_rss_auth :index accept_api_auth :index, :show, :create, :update, :destroy require_sudo_mode :destroy - after_filter :only => [:create, :edit, :update, :archive, :unarchive, :destroy] do |controller| - if controller.request.post? - controller.send :expire_action, :controller => 'welcome', :action => 'robots' - end - end - helper :custom_fields helper :issues helper :queries @@ -41,6 +36,11 @@ class ProjectsController < ApplicationController # Lists visible projects def index + # try to redirect to the requested menu item + if params[:jump] && redirect_to_menu_item(params[:jump]) + return + end + scope = Project.visible.sorted respond_to do |format| @@ -62,6 +62,18 @@ class ProjectsController < ApplicationController end end + def autocomplete + respond_to do |format| + format.js { + if params[:q].present? + @projects = Project.visible.like(params[:q]).to_a + else + @projects = User.current.projects.to_a + end + } + end + end + def new @issue_custom_fields = IssueCustomField.sorted.to_a @trackers = Tracker.sorted.to_a @@ -161,6 +173,10 @@ class ProjectsController < ApplicationController @issue_category ||= IssueCategory.new @member ||= @project.members.new @trackers = Tracker.sorted.to_a + + @version_status = params[:version_status] || 'open' + @version_name = params[:version_name] + @versions = @project.shared_versions.status(@version_status).like(@version_name) @wiki ||= @project.wiki || Wiki.new(:project => @project) end @@ -198,14 +214,14 @@ class ProjectsController < ApplicationController unless @project.archive flash[:error] = l(:error_can_not_archive_project) end - redirect_to admin_projects_path(:status => params[:status]) + redirect_to_referer_or admin_projects_path(:status => params[:status]) end def unarchive unless @project.active? @project.unarchive end - redirect_to admin_projects_path(:status => params[:status]) + redirect_to_referer_or admin_projects_path(:status => params[:status]) end def close diff --git a/app/controllers/queries_controller.rb b/app/controllers/queries_controller.rb index 9b14d44eb..54f695fd7 100644 --- a/app/controllers/queries_controller.rb +++ b/app/controllers/queries_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,8 +17,8 @@ class QueriesController < ApplicationController menu_item :issues - before_filter :find_query, :except => [:new, :create, :index] - before_filter :find_optional_project, :only => [:new, :create] + before_action :find_query, :only => [:edit, :update, :destroy] + before_action :find_optional_project, :only => [:new, :create] accept_api_auth :index @@ -31,9 +31,10 @@ class QueriesController < ApplicationController else @limit = per_page_option end - @query_count = IssueQuery.visible.count + scope = query_class.visible + @query_count = scope.count @query_pages = Paginator.new @query_count, @limit, params['page'] - @queries = IssueQuery.visible. + @queries = scope. order("#{Query.table_name}.name"). limit(@limit). offset(@offset). @@ -45,21 +46,21 @@ class QueriesController < ApplicationController end def new - @query = IssueQuery.new + @query = query_class.new @query.user = User.current @query.project = @project @query.build_from_params(params) end def create - @query = IssueQuery.new + @query = query_class.new @query.user = User.current @query.project = @project update_query_from_params if @query.save flash[:notice] = l(:notice_successful_create) - redirect_to_issues(:query_id => @query) + redirect_to_items(:query_id => @query) else render :action => 'new', :layout => !request.xhr? end @@ -73,7 +74,7 @@ class QueriesController < ApplicationController if @query.save flash[:notice] = l(:notice_successful_update) - redirect_to_issues(:query_id => @query) + redirect_to_items(:query_id => @query) else render :action => 'edit' end @@ -81,12 +82,32 @@ class QueriesController < ApplicationController def destroy @query.destroy - redirect_to_issues(:set_filter => 1) + redirect_to_items(:set_filter => 1) end -private + # Returns the values for a query filter + def filter + q = query_class.new + if params[:project_id].present? + q.project = Project.find(params[:project_id]) + end + + unless User.current.allowed_to?(q.class.view_permission, q.project, :global => true) + raise Unauthorized + end + + filter = q.available_filters[params[:name].to_s] + values = filter ? filter.values : [] + + render :json => values + rescue ActiveRecord::RecordNotFound + render_404 + end + + private + def find_query - @query = IssueQuery.find(params[:id]) + @query = Query.find(params[:id]) @project = @query.project render_403 unless @query.editable_by?(User.current) rescue ActiveRecord::RecordNotFound @@ -107,15 +128,20 @@ private @query.sort_criteria = params[:query] && params[:query][:sort_criteria] @query.name = params[:query] && params[:query][:name] if User.current.allowed_to?(:manage_public_queries, @query.project) || User.current.admin? - @query.visibility = (params[:query] && params[:query][:visibility]) || IssueQuery::VISIBILITY_PRIVATE + @query.visibility = (params[:query] && params[:query][:visibility]) || Query::VISIBILITY_PRIVATE @query.role_ids = params[:query] && params[:query][:role_ids] else - @query.visibility = IssueQuery::VISIBILITY_PRIVATE + @query.visibility = Query::VISIBILITY_PRIVATE end @query end - def redirect_to_issues(options) + def redirect_to_items(options) + method = "redirect_to_#{@query.class.name.underscore}" + send method, options + end + + def redirect_to_issue_query(options) if params[:gantt] if @project redirect_to project_gantt_path(@project, options) @@ -126,4 +152,14 @@ private redirect_to _project_issues_path(@project, options) end end + + def redirect_to_time_entry_query(options) + redirect_to _time_entries_path(@project, nil, options) + end + + # Returns the Query subclass, IssueQuery by default + # for compatibility with previous behaviour + def query_class + Query.get_subclass(params[:type] || 'IssueQuery') + end end diff --git a/app/controllers/reports_controller.rb b/app/controllers/reports_controller.rb index 20279871e..08c30efe8 100644 --- a/app/controllers/reports_controller.rb +++ b/app/controllers/reports_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,7 +17,7 @@ class ReportsController < ApplicationController menu_item :issues - before_filter :find_project, :authorize, :find_issue_statuses + before_action :find_project, :authorize, :find_issue_statuses def issue_report @trackers = @project.rolled_up_trackers(false).visible @@ -76,14 +76,8 @@ class ReportsController < ApplicationController @rows = @project.descendants.visible @data = Issue.by_subproject(@project) || [] @report_title = l(:field_subproject) - end - - respond_to do |format| - if @field - format.html {} - else - format.html { redirect_to :action => 'issue_report', :id => @project } - end + else + render_404 end end diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index 8da996ee2..29a3b5937 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -28,31 +28,22 @@ class RepositoriesController < ApplicationController menu_item :settings, :only => [:new, :create, :edit, :update, :destroy, :committers] default_search_scope :changesets - before_filter :find_project_by_project_id, :only => [:new, :create] - before_filter :find_repository, :only => [:edit, :update, :destroy, :committers] - before_filter :find_project_repository, :except => [:new, :create, :edit, :update, :destroy, :committers] - before_filter :find_changeset, :only => [:revision, :add_related_issue, :remove_related_issue] - before_filter :authorize + before_action :find_project_by_project_id, :only => [:new, :create] + before_action :build_new_repository_from_params, :only => [:new, :create] + before_action :find_repository, :only => [:edit, :update, :destroy, :committers] + before_action :find_project_repository, :except => [:new, :create, :edit, :update, :destroy, :committers] + before_action :find_changeset, :only => [:revision, :add_related_issue, :remove_related_issue] + before_action :authorize accept_rss_auth :revisions rescue_from Redmine::Scm::Adapters::CommandFailed, :with => :show_error_command_failed def new - scm = params[:repository_scm] || (Redmine::Scm::Base.all & Setting.enabled_scm).first - @repository = Repository.factory(scm) @repository.is_default = @project.repository.nil? - @repository.project = @project end def create - attrs = pickup_extra_info - @repository = Repository.factory(params[:repository_scm]) - @repository.safe_attributes = params[:repository] - if attrs[:attrs_extra].keys.any? - @repository.merge_extra_info(attrs[:attrs_extra]) - end - @repository.project = @project - if request.post? && @repository.save + if @repository.save redirect_to settings_project_path(@project, :tab => 'repositories') else render :action => 'new' @@ -63,12 +54,7 @@ class RepositoriesController < ApplicationController end def update - attrs = pickup_extra_info - @repository.safe_attributes = attrs[:attrs] - if attrs[:attrs_extra].keys.any? - @repository.merge_extra_info(attrs[:attrs_extra]) - end - @repository.project = @project + @repository.safe_attributes = params[:repository] if @repository.save redirect_to settings_project_path(@project, :tab => 'repositories') else @@ -76,20 +62,6 @@ class RepositoriesController < ApplicationController end end - def pickup_extra_info - p = {} - p_extra = {} - params[:repository].each do |k, v| - if k =~ /^extra_/ - p_extra[k] = v - else - p[k] = v - end - end - {:attrs => p, :attrs_extra => p_extra} - end - private :pickup_extra_info - def committers @committers = @repository.committers @users = @project.users.to_a @@ -97,7 +69,7 @@ class RepositoriesController < ApplicationController @users += User.where(:id => additional_user_ids).to_a unless additional_user_ids.empty? @users.compact! @users.sort! - if request.post? && params[:committers].is_a?(Hash) + if request.post? && params[:committers].present? # Build a hash with repository usernames as keys and corresponding user ids as values @repository.committer_ids = params[:committers].values.inject({}) {|h, c| h[c.first] = c.last; h} flash[:notice] = l(:notice_successful_update) @@ -116,7 +88,7 @@ class RepositoriesController < ApplicationController @entries = @repository.entries(@path, @rev) @changeset = @repository.find_changeset_by_name(@rev) if request.xhr? - @entries ? render(:partial => 'dir_list_content') : render(:nothing => true) + @entries ? render(:partial => 'dir_list_content') : head(200) else (show_error_not_found; return) unless @entries @changesets = @repository.latest_changesets(@path, @rev) @@ -200,7 +172,7 @@ class RepositoriesController < ApplicationController return true if Redmine::MimeType.is_type?('text', path) # Ruby 1.8.6 has a bug of integer divisions. # http://apidock.com/ruby/v1_8_6_287/String/is_binary_data%3F - return false if ent.is_binary_data? + return false if Redmine::Scm::Adapters::ScmData.binary?(ent) true end private :is_entry_text_data? @@ -211,14 +183,17 @@ class RepositoriesController < ApplicationController @annotate = @repository.scm.annotate(@path, @rev) if @annotate.nil? || @annotate.empty? - (render_error l(:error_scm_annotate); return) - end - ann_buf_size = 0 - @annotate.lines.each do |buf| - ann_buf_size += buf.size - end - if ann_buf_size > Setting.file_max_size_displayed.to_i.kilobyte - (render_error l(:error_scm_annotate_big_text_file); return) + @annotate = nil + @error_message = l(:error_scm_annotate) + else + ann_buf_size = 0 + @annotate.lines.each do |buf| + ann_buf_size += buf.size + end + if ann_buf_size > Setting.file_max_size_displayed.to_i.kilobyte + @annotate = nil + @error_message = l(:error_scm_annotate_big_text_file) + end end @changeset = @repository.find_changeset_by_name(@rev) end @@ -305,6 +280,18 @@ class RepositoriesController < ApplicationController private + def build_new_repository_from_params + scm = params[:repository_scm] || (Redmine::Scm::Base.all & Setting.enabled_scm).first + unless @repository = Repository.factory(scm) + render_404 + return + end + + @repository.project = @project + @repository.safe_attributes = params[:repository] + @repository + end + def find_repository @repository = Repository.find(params[:id]) @project = @repository.project diff --git a/app/controllers/roles_controller.rb b/app/controllers/roles_controller.rb index 2de185b5b..b85c51b62 100644 --- a/app/controllers/roles_controller.rb +++ b/app/controllers/roles_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,10 +17,11 @@ class RolesController < ApplicationController layout 'admin' + self.main_menu = false - before_filter :require_admin, :except => [:index, :show] - before_filter :require_admin_or_api_request, :only => [:index, :show] - before_filter :find_role, :only => [:show, :edit, :update, :destroy] + before_action :require_admin, :except => [:index, :show] + before_action :require_admin_or_api_request, :only => [:index, :show] + before_action :find_role, :only => [:show, :edit, :update, :destroy] accept_api_auth :index, :show require_sudo_mode :create, :update, :destroy @@ -45,7 +46,8 @@ class RolesController < ApplicationController def new # Prefills the form with 'Non member' role permissions by default - @role = Role.new(params[:role] || {:permissions => Role.non_member.permissions}) + @role = Role.new + @role.safe_attributes = params[:role] || {:permissions => Role.non_member.permissions} if params[:copy].present? && @copy_from = Role.find_by_id(params[:copy]) @role.copy_from(@copy_from) end @@ -53,11 +55,12 @@ class RolesController < ApplicationController end def create - @role = Role.new(params[:role]) + @role = Role.new + @role.safe_attributes = params[:role] if request.post? && @role.save # workflow copy if !params[:copy_workflow_from].blank? && (copy_from = Role.find_by_id(params[:copy_workflow_from])) - @role.workflow_rules.copy(copy_from) + @role.copy_workflow_rules(copy_from) end flash[:notice] = l(:notice_successful_create) redirect_to roles_path @@ -71,27 +74,29 @@ class RolesController < ApplicationController end def update - if @role.update_attributes(params[:role]) + @role.safe_attributes = params[:role] + if @role.save respond_to do |format| format.html { flash[:notice] = l(:notice_successful_update) redirect_to roles_path(:page => params[:page]) } - format.js { render :nothing => true } + format.js { head 200 } end else respond_to do |format| format.html { render :action => 'edit' } - format.js { render :nothing => true, :status => 422 } + format.js { head 422 } end end end def destroy - @role.destroy - redirect_to roles_path - rescue - flash[:error] = l(:error_can_not_remove_role) + begin + @role.destroy + rescue + flash[:error] = l(:error_can_not_remove_role) + end redirect_to roles_path end diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index cc1970ef1..486029fc9 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,7 +16,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class SearchController < ApplicationController - before_filter :find_optional_project + before_action :find_optional_project accept_api_auth :index def index @@ -49,7 +49,7 @@ class SearchController < ApplicationController when 'my_projects' User.current.projects when 'subprojects' - @project ? (@project.self_and_descendants.active.to_a) : nil + @project ? (@project.self_and_descendants.to_a) : nil else @project end diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb index 9552efb9b..7b2dceb31 100644 --- a/app/controllers/settings_controller.rb +++ b/app/controllers/settings_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,11 +17,12 @@ class SettingsController < ApplicationController layout 'admin' + self.main_menu = false menu_item :plugins, :only => :plugin helper :queries - before_filter :require_admin + before_action :require_admin require_sudo_mode :index, :edit, :plugin @@ -32,24 +33,30 @@ class SettingsController < ApplicationController def edit @notifiables = Redmine::Notifiable.all - if request.post? && params[:settings] && params[:settings].is_a?(Hash) - Setting.set_all_from_params(params[:settings]) - flash[:notice] = l(:notice_successful_update) - redirect_to settings_path(:tab => params[:tab]) - else - @options = {} - user_format = User::USER_FORMATS.collect{|key, value| [key, value[:setting_order]]}.sort{|a, b| a[1] <=> b[1]} - @options[:user_format] = user_format.collect{|f| [User.current.name(f[0]), f[0].to_s]} - @deliveries = ActionMailer::Base.perform_deliveries - - @guessed_host_and_path = request.host_with_port.dup - @guessed_host_and_path << ('/'+ Redmine::Utils.relative_url_root.gsub(%r{^\/}, '')) unless Redmine::Utils.relative_url_root.blank? - - @commit_update_keywords = Setting.commit_update_keywords.dup - @commit_update_keywords = [{}] unless @commit_update_keywords.is_a?(Array) && @commit_update_keywords.any? - - Redmine::Themes.rescan + if request.post? + errors = Setting.set_all_from_params(params[:settings]) + if errors.blank? + flash[:notice] = l(:notice_successful_update) + redirect_to settings_path(:tab => params[:tab]) + return + else + @setting_errors = errors + # render the edit form with error messages + end end + + @options = {} + user_format = User::USER_FORMATS.collect{|key, value| [key, value[:setting_order]]}.sort{|a, b| a[1] <=> b[1]} + @options[:user_format] = user_format.collect{|f| [User.current.name(f[0]), f[0].to_s]} + @deliveries = ActionMailer::Base.perform_deliveries + + @guessed_host_and_path = request.host_with_port.dup + @guessed_host_and_path << ('/'+ Redmine::Utils.relative_url_root.gsub(%r{^\/}, '')) unless Redmine::Utils.relative_url_root.blank? + + @commit_update_keywords = Setting.commit_update_keywords.dup + @commit_update_keywords = [{}] unless @commit_update_keywords.is_a?(Array) && @commit_update_keywords.any? + + Redmine::Themes.rescan end def plugin @@ -60,7 +67,8 @@ class SettingsController < ApplicationController end if request.post? - Setting.send "plugin_#{@plugin.id}=", params[:settings] + setting = params[:settings] ? params[:settings].permit!.to_h : {} + Setting.send "plugin_#{@plugin.id}=", setting flash[:notice] = l(:notice_successful_update) redirect_to plugin_settings_path(@plugin) else diff --git a/app/controllers/sys_controller.rb b/app/controllers/sys_controller.rb index 9231153b6..bd8238a6a 100644 --- a/app/controllers/sys_controller.rb +++ b/app/controllers/sys_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,13 +16,13 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class SysController < ActionController::Base - before_filter :check_enabled + before_action :check_enabled def projects p = Project.active.has_module(:repository). order("#{Project.table_name}.identifier").preload(:repository).to_a # extra_info attribute from repository breaks activeresource client - render :xml => p.to_xml( + render :json => p.to_json( :only => [:id, :identifier, :name, :is_public, :status], :include => {:repository => {:only => [:id, :url]}} ) @@ -31,15 +31,16 @@ class SysController < ActionController::Base def create_project_repository project = Project.find(params[:id]) if project.repository - render :nothing => true, :status => 409 + head 409 else logger.info "Repository for #{project.name} was reported to be created by #{request.remote_ip}." - repository = Repository.factory(params[:vendor], params[:repository]) + repository = Repository.factory(params[:vendor]) + repository.safe_attributes = params[:repository] repository.project = project if repository.save - render :xml => {repository.class.name.underscore.gsub('/', '-') => {:id => repository.id, :url => repository.url}}, :status => 201 + render :json => {repository.class.name.underscore.gsub('/', '-') => {:id => repository.id, :url => repository.url}}, :status => 201 else - render :nothing => true, :status => 422 + head 422 end end end @@ -64,9 +65,9 @@ class SysController < ActionController::Base repository.fetch_changesets end end - render :nothing => true, :status => 200 + head 200 rescue ActiveRecord::RecordNotFound - render :nothing => true, :status => 404 + head 404 end protected @@ -74,7 +75,7 @@ class SysController < ActionController::Base def check_enabled User.current = nil unless Setting.sys_api_enabled? && params[:key].to_s == Setting.sys_api_key - render :text => 'Access denied. Repository management WS is disabled or key is invalid.', :status => 403 + render :plain => 'Access denied. Repository management WS is disabled or key is invalid.', :status => 403 return false end end diff --git a/app/controllers/timelog_controller.rb b/app/controllers/timelog_controller.rb index aecb8fb98..df6b618bd 100644 --- a/app/controllers/timelog_controller.rb +++ b/app/controllers/timelog_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,23 +16,22 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class TimelogController < ApplicationController - menu_item :issues + menu_item :time_entries - before_filter :find_time_entry, :only => [:show, :edit, :update] - before_filter :check_editability, :only => [:edit, :update] - before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy] - before_filter :authorize, :only => [:show, :edit, :update, :bulk_edit, :bulk_update, :destroy] + before_action :find_time_entry, :only => [:show, :edit, :update] + before_action :check_editability, :only => [:edit, :update] + before_action :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy] + before_action :authorize, :only => [:show, :edit, :update, :bulk_edit, :bulk_update, :destroy] - before_filter :find_optional_project, :only => [:new, :create, :index, :report] - before_filter :authorize_global, :only => [:new, :create, :index, :report] + before_action :find_optional_issue, :only => [:new, :create] + before_action :find_optional_project, :only => [:index, :report] + before_action :authorize_global, :only => [:new, :create, :index, :report] accept_rss_auth :index accept_api_auth :index, :show, :create, :update, :destroy rescue_from Query::StatementInvalid, :with => :query_statement_invalid - helper :sort - include SortHelper helper :issues include TimelogHelper helper :custom_fields @@ -41,20 +40,16 @@ class TimelogController < ApplicationController include QueriesHelper def index - @query = TimeEntryQuery.build_from_params(params, :project => @project, :name => '_') - - sort_init(@query.sort_criteria.empty? ? [['spent_on', 'desc']] : @query.sort_criteria) - sort_update(@query.sortable_columns) - scope = time_entry_scope(:order => sort_clause). - includes(:project, :user, :issue). - preload(:issue => [:project, :tracker, :status, :assigned_to, :priority]) + retrieve_time_entry_query + scope = time_entry_scope. + preload(:issue => [:project, :tracker, :status, :assigned_to, :priority]). + preload(:project, :user) respond_to do |format| format.html { @entry_count = scope.count @entry_pages = Paginator.new @entry_count, per_page_option, params['page'] @entries = scope.offset(@entry_pages.offset).limit(@entry_pages.per_page).to_a - @total_hours = scope.sum(:hours).to_f render :layout => !request.xhr? } @@ -76,7 +71,7 @@ class TimelogController < ApplicationController end def report - @query = TimeEntryQuery.build_from_params(params, :project => @project, :name => '_') + retrieve_time_entry_query scope = time_entry_scope @report = Redmine::Helpers::TimeReport.new(@project, @issue, params[:criteria], params[:columns], scope) @@ -90,7 +85,7 @@ class TimelogController < ApplicationController def show respond_to do |format| # TODO: Implement html response - format.html { render :nothing => true, :status => 406 } + format.html { head 406 } format.api end end @@ -172,25 +167,39 @@ class TimelogController < ApplicationController def bulk_edit @available_activities = @projects.map(&:activities).reduce(:&) - @custom_fields = TimeEntry.first.available_custom_fields + @custom_fields = TimeEntry.first.available_custom_fields.select {|field| field.format.bulk_edit_supported} end def bulk_update attributes = parse_params_for_bulk_update(params[:time_entry]) - unsaved_time_entry_ids = [] + unsaved_time_entries = [] + saved_time_entries = [] + @time_entries.each do |time_entry| time_entry.reload time_entry.safe_attributes = attributes call_hook(:controller_time_entries_bulk_edit_before_save, { :params => params, :time_entry => time_entry }) - unless time_entry.save - logger.info "time entry could not be updated: #{time_entry.errors.full_messages}" if logger && logger.info? - # Keep unsaved time_entry ids to display them in flash error - unsaved_time_entry_ids << time_entry.id + if time_entry.save + saved_time_entries << time_entry + else + unsaved_time_entries << time_entry end end - set_flash_from_bulk_time_entry_save(@time_entries, unsaved_time_entry_ids) - redirect_back_or_default project_time_entries_path(@projects.first) + + if unsaved_time_entries.empty? + flash[:notice] = l(:notice_successful_update) unless saved_time_entries.empty? + redirect_back_or_default project_time_entries_path(@projects.first) + else + @saved_time_entries = @time_entries + @unsaved_time_entries = unsaved_time_entries + @time_entries = TimeEntry.where(:id => unsaved_time_entries.map(&:id)). + preload(:project => :time_entry_activities). + preload(:user).to_a + + bulk_edit + render :action => 'bulk_edit' + end end def destroy @@ -249,22 +258,17 @@ private render_404 end - def set_flash_from_bulk_time_entry_save(time_entries, unsaved_time_entry_ids) - if unsaved_time_entry_ids.empty? - flash[:notice] = l(:notice_successful_update) unless time_entries.empty? + def find_optional_issue + if params[:issue_id].present? + @issue = Issue.find(params[:issue_id]) + @project = @issue.project else - flash[:error] = l(:notice_failed_to_save_time_entries, - :count => unsaved_time_entry_ids.size, - :total => time_entries.size, - :ids => '#' + unsaved_time_entry_ids.join(', #')) + find_optional_project end end def find_optional_project - if params[:issue_id].present? - @issue = Issue.find(params[:issue_id]) - @project = @issue.project - elsif params[:project_id].present? + if params[:project_id].present? @project = Project.find(params[:project_id]) end rescue ActiveRecord::RecordNotFound @@ -273,10 +277,10 @@ private # Returns the TimeEntry scope for index and report actions def time_entry_scope(options={}) - scope = @query.results_scope(options) - if @issue - scope = scope.on_issue(@issue) - end - scope + @query.results_scope(options) + end + + def retrieve_time_entry_query + retrieve_query(TimeEntryQuery, false) end end diff --git a/app/controllers/trackers_controller.rb b/app/controllers/trackers_controller.rb index 5401bae65..caf6b1165 100644 --- a/app/controllers/trackers_controller.rb +++ b/app/controllers/trackers_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,9 +17,10 @@ class TrackersController < ApplicationController layout 'admin' + self.main_menu = false - before_filter :require_admin, :except => :index - before_filter :require_admin_or_api_request, :only => :index + before_action :require_admin, :except => :index + before_action :require_admin_or_api_request, :only => :index accept_api_auth :index def index @@ -31,17 +32,19 @@ class TrackersController < ApplicationController end def new - @tracker ||= Tracker.new(params[:tracker]) + @tracker ||= Tracker.new + @tracker.safe_attributes = params[:tracker] @trackers = Tracker.sorted.to_a @projects = Project.all end def create - @tracker = Tracker.new(params[:tracker]) + @tracker = Tracker.new + @tracker.safe_attributes = params[:tracker] if @tracker.save # workflow copy if !params[:copy_workflow_from].blank? && (copy_from = Tracker.find_by_id(params[:copy_workflow_from])) - @tracker.workflow_rules.copy(copy_from) + @tracker.copy_workflow_rules(copy_from) end flash[:notice] = l(:notice_successful_create) redirect_to trackers_path @@ -58,13 +61,14 @@ class TrackersController < ApplicationController def update @tracker = Tracker.find(params[:id]) - if @tracker.update_attributes(params[:tracker]) + @tracker.safe_attributes = params[:tracker] + if @tracker.save respond_to do |format| format.html { flash[:notice] = l(:notice_successful_update) redirect_to trackers_path(:page => params[:page]) } - format.js { render :nothing => true } + format.js { head 200 } end else respond_to do |format| @@ -72,7 +76,7 @@ class TrackersController < ApplicationController edit render :action => 'edit' } - format.js { render :nothing => true, :status => 422 } + format.js { head 422 } end end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 78642c7c7..0133f9797 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,9 +17,11 @@ class UsersController < ApplicationController layout 'admin' + self.main_menu = false - before_filter :require_admin, :except => :show - before_filter :find_user, :only => [:show, :edit, :update, :destroy] + before_action :require_admin, :except => :show + before_action ->{ find_user(false) }, :only => :show + before_action :find_user, :only => [:edit, :update, :destroy] accept_api_auth :index, :show, :create, :update, :destroy helper :sort @@ -68,7 +70,7 @@ class UsersController < ApplicationController end # show projects based on current user visibility - @memberships = @user.memberships.where(Project.visible_condition(User.current)).to_a + @memberships = @user.memberships.preload(:roles, :project).where(Project.visible_condition(User.current)).to_a respond_to do |format| format.html { @@ -87,12 +89,10 @@ class UsersController < ApplicationController end def create - @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option) + @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option, :admin => false) @user.safe_attributes = params[:user] - @user.admin = params[:user][:admin] || false - @user.login = params[:user][:login] @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] unless @user.auth_source_id - @user.pref.attributes = params[:pref] if params[:pref] + @user.pref.safe_attributes = params[:pref] if @user.save Mailer.account_information(@user, @user.password).deliver if params[:send_information] @@ -127,8 +127,6 @@ class UsersController < ApplicationController end def update - @user.admin = params[:user][:admin] if params[:user][:admin] - @user.login = params[:user][:login] if params[:user][:login] if params[:user][:password].present? && (@user.auth_source_id.nil? || params[:user][:auth_source_id].blank?) @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] end @@ -136,14 +134,14 @@ class UsersController < ApplicationController # Was the account actived ? (do it before User#save clears the change) was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE]) # TODO: Similar to My#account - @user.pref.attributes = params[:pref] if params[:pref] + @user.pref.safe_attributes = params[:pref] if @user.save @user.pref.save if was_activated Mailer.account_activated(@user).deliver - elsif @user.active? && params[:send_information] && @user.password.present? && @user.auth_source_id.nil? && @user != User.current + elsif @user.active? && params[:send_information] && @user != User.current Mailer.account_information(@user, @user.password).deliver end @@ -177,10 +175,12 @@ class UsersController < ApplicationController private - def find_user + def find_user(logged = true) if params[:id] == 'current' require_login || return @user = User.current + elsif logged + @user = User.logged.find(params[:id]) else @user = User.find(params[:id]) end diff --git a/app/controllers/versions_controller.rb b/app/controllers/versions_controller.rb index beb0f16a3..a5364169e 100644 --- a/app/controllers/versions_controller.rb +++ b/app/controllers/versions_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -18,10 +18,10 @@ class VersionsController < ApplicationController menu_item :roadmap model_object Version - before_filter :find_model_object, :except => [:index, :new, :create, :close_completed] - before_filter :find_project_from_association, :except => [:index, :new, :create, :close_completed] - before_filter :find_project_by_project_id, :only => [:index, :new, :create, :close_completed] - before_filter :authorize + before_action :find_model_object, :except => [:index, :new, :create, :close_completed] + before_action :find_project_from_association, :except => [:index, :new, :create, :close_completed] + before_action :find_project_by_project_id, :only => [:index, :new, :create, :close_completed] + before_action :authorize accept_api_auth :index, :show, :create, :update, :destroy @@ -38,9 +38,9 @@ class VersionsController < ApplicationController @versions = @project.shared_versions.preload(:custom_values) @versions += @project.rolled_up_versions.visible.preload(:custom_values) if @with_subprojects - @versions = @versions.uniq.sort + @versions = @versions.to_a.uniq.sort unless params[:completed] - @completed_versions = @versions.select(&:completed?) + @completed_versions = @versions.select(&:completed?).reverse @versions -= @completed_versions end @@ -66,6 +66,7 @@ class VersionsController < ApplicationController format.html { @issues = @version.fixed_issues.visible. includes(:status, :tracker, :priority). + preload(:project). reorder("#{Tracker.table_name}.position, #{Issue.table_name}.id"). to_a } diff --git a/app/controllers/watchers_controller.rb b/app/controllers/watchers_controller.rb index d9f2351d0..3f080e96b 100644 --- a/app/controllers/watchers_controller.rb +++ b/app/controllers/watchers_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,7 +16,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class WatchersController < ApplicationController - before_filter :require_login, :find_watchables, :only => [:watch, :unwatch] + before_action :require_login, :find_watchables, :only => [:watch, :unwatch] def watch set_watcher(@watchables, User.current, true) @@ -26,7 +26,7 @@ class WatchersController < ApplicationController set_watcher(@watchables, User.current, false) end - before_filter :find_project, :authorize, :only => [:new, :create, :append, :destroy, :autocomplete_for_user] + before_action :find_project, :authorize, :only => [:new, :create, :append, :destroy, :autocomplete_for_user] accept_api_auth :create, :destroy def new @@ -35,7 +35,7 @@ class WatchersController < ApplicationController def create user_ids = [] - if params[:watcher].is_a?(Hash) + if params[:watcher] user_ids << (params[:watcher][:user_ids] || params[:watcher][:user_id]) else user_ids << params[:user_id] @@ -47,19 +47,19 @@ class WatchersController < ApplicationController end end respond_to do |format| - format.html { redirect_to_referer_or {render :text => 'Watcher added.', :layout => true}} + format.html { redirect_to_referer_or {render :html => 'Watcher added.', :status => 200, :layout => true}} format.js { @users = users_for_new_watcher } format.api { render_api_ok } end end def append - if params[:watcher].is_a?(Hash) + if params[:watcher] user_ids = params[:watcher][:user_ids] || [params[:watcher][:user_id]] @users = User.active.visible.where(:id => user_ids).to_a end if @users.blank? - render :nothing => true + head 200 end end @@ -69,7 +69,7 @@ class WatchersController < ApplicationController watchable.set_watcher(user, false) end respond_to do |format| - format.html { redirect_to :back } + format.html { redirect_to_referer_or {render :html => 'Watcher removed.', :status => 200, :layout => true} } format.js format.api { render_api_ok } end @@ -108,7 +108,10 @@ class WatchersController < ApplicationController watchable.set_watcher(user, watching) end respond_to do |format| - format.html { redirect_to_referer_or {render :text => (watching ? 'Watcher added.' : 'Watcher removed.'), :layout => true}} + format.html { + text = watching ? 'Watcher added.' : 'Watcher removed.' + redirect_to_referer_or {render :html => text, :status => 200, :layout => true} + } format.js { render :partial => 'set_watcher', :locals => {:user => user, :watched => watchables} } end end diff --git a/app/controllers/welcome_controller.rb b/app/controllers/welcome_controller.rb index 717c72916..341a64df8 100644 --- a/app/controllers/welcome_controller.rb +++ b/app/controllers/welcome_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,7 +16,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class WelcomeController < ApplicationController - caches_action :robots + self.main_menu = false def index @news = News.latest User.current diff --git a/app/controllers/wiki_controller.rb b/app/controllers/wiki_controller.rb index ebf7a1884..5d9a91327 100644 --- a/app/controllers/wiki_controller.rb +++ b/app/controllers/wiki_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -31,11 +31,11 @@ # TODO: still being worked on class WikiController < ApplicationController default_search_scope :wiki_pages - before_filter :find_wiki, :authorize - before_filter :find_existing_or_new_page, :only => [:show, :edit, :update] - before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy, :destroy_version] + before_action :find_wiki, :authorize + before_action :find_existing_or_new_page, :only => [:show, :edit, :update] + before_action :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy, :destroy_version] + before_action :find_attachments, :only => [:preview] accept_api_auth :index, :show, :update, :destroy - before_filter :find_attachments, :only => [:preview] helper :attachments include AttachmentsHelper @@ -95,6 +95,9 @@ class WikiController < ApplicationController end return end + + call_hook :controller_wiki_show_before_render, content: @content, format: params[:format] + if User.current.allowed_to?(:export_wiki_pages, @project) if params[:format] == 'pdf' send_file_headers! :type => 'application/pdf', :filename => filename_for_content_disposition("#{@page.title}.pdf") @@ -153,7 +156,7 @@ class WikiController < ApplicationController @content = @page.content || WikiContent.new(:page => @page) content_params = params[:content] - if content_params.nil? && params[:wiki_page].is_a?(Hash) + if content_params.nil? && params[:wiki_page].present? content_params = params[:wiki_page].slice(:text, :comments, :version) end content_params ||= {} diff --git a/app/controllers/wikis_controller.rb b/app/controllers/wikis_controller.rb index 61abfaf65..c61c2f946 100644 --- a/app/controllers/wikis_controller.rb +++ b/app/controllers/wikis_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,7 +17,7 @@ class WikisController < ApplicationController menu_item :settings - before_filter :find_project, :authorize + before_action :find_project, :authorize # Create or update a project's wiki def edit diff --git a/app/controllers/workflows_controller.rb b/app/controllers/workflows_controller.rb index 7f92f2546..c831827cb 100644 --- a/app/controllers/workflows_controller.rb +++ b/app/controllers/workflows_controller.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,8 +17,9 @@ class WorkflowsController < ApplicationController layout 'admin' + self.main_menu = false - before_filter :require_admin + before_action :require_admin def index @roles = Role.sorted.select(&:consider_workflow?) @@ -137,7 +138,11 @@ class WorkflowsController < ApplicationController def find_statuses @used_statuses_only = (params[:used_statuses_only] == '0' ? false : true) if @trackers && @used_statuses_only - @statuses = @trackers.map(&:issue_statuses).flatten.uniq.sort.presence + role_ids = Role.all.select(&:consider_workflow?).map(&:id) + status_ids = WorkflowTransition.where( + :tracker_id => @trackers.map(&:id), :role_id => role_ids + ).distinct.pluck(:old_status_id, :new_status_id).flatten.uniq + @statuses = IssueStatus.where(:id => status_ids).sorted.to_a.presence end @statuses ||= IssueStatus.sorted.to_a end diff --git a/app/helpers/account_helper.rb b/app/helpers/account_helper.rb index 0fedbe2ad..e4d8222bf 100644 --- a/app/helpers/account_helper.rb +++ b/app/helpers/account_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/activities_helper.rb b/app/helpers/activities_helper.rb index 8721227c2..54e5580a2 100644 --- a/app/helpers/activities_helper.rb +++ b/app/helpers/activities_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/admin_helper.rb b/app/helpers/admin_helper.rb index b8ac260e5..e972ef03d 100644 --- a/app/helpers/admin_helper.rb +++ b/app/helpers/admin_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 76b789b45..792af987c 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -198,6 +198,8 @@ module ApplicationHelper l(:general_text_No) when 'Issue' object.visible? && html ? link_to_issue(object) : "##{object.id}" + when 'Attachment' + html ? link_to_attachment(object) : object.filename when 'CustomValue', 'CustomFieldValue' if object.custom_field f = object.custom_field.format.formatted_custom_value(self, object, html) @@ -219,20 +221,32 @@ module ApplicationHelper end def thumbnail_tag(attachment) - link_to image_tag(thumbnail_path(attachment)), - named_attachment_path(attachment, attachment.filename), + thumbnail_size = Setting.thumbnails_size.to_i + link_to( + image_tag( + thumbnail_path(attachment), + :srcset => "#{thumbnail_path(attachment, :size => thumbnail_size * 2)} 2x", + :style => "max-width: #{thumbnail_size}px; max-height: #{thumbnail_size}px;" + ), + named_attachment_path( + attachment, + attachment.filename + ), :title => attachment.filename + ) end def toggle_link(name, id, options={}) onclick = "$('##{id}').toggle(); " onclick << (options[:focus] ? "$('##{options[:focus]}').focus(); " : "this.blur(); ") + onclick << "$(window).scrollTop($('##{options[:focus]}').position().top); " if options[:scroll] onclick << "return false;" link_to(name, "#", :onclick => onclick) end + # Used to format item titles on the activity view def format_activity_title(text) - h(truncate_single_line_raw(text, 100)) + text end def format_activity_day(date) @@ -252,6 +266,11 @@ module ApplicationHelper end end + def format_changeset_comments(changeset, options={}) + method = options[:short] ? :short_comments : :comments + textilizable changeset, method, :formatting => Setting.commit_logs_formatting? + end + def due_date_distance_in_words(date) if date l((date < User.current.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(User.current.today, date)) @@ -329,22 +348,54 @@ module ApplicationHelper end end + # Returns the default scope for the quick search form + # Could be 'all', 'my_projects', 'subprojects' or nil (current project) + def default_search_project_scope + if @project && !@project.leaf? + 'subprojects' + end + end + + # Returns an array of projects that are displayed in the quick-jump box + def projects_for_jump_box(user=User.current) + if user.logged? + user.projects.active.select(:id, :name, :identifier, :lft, :rgt).to_a + else + [] + end + end + + def render_projects_for_jump_box(projects, selected=nil) + jump = params[:jump].presence || current_menu_item + s = ''.html_safe + project_tree(projects) do |project, level| + padding = level * 16 + text = content_tag('span', project.name, :style => "padding-left:#{padding}px;") + s << link_to(text, project_path(project, :jump => jump), :title => project.name, :class => (project == selected ? 'selected' : nil)) + end + s + end + # Renders the project quick-jump box def render_project_jump_box - return unless User.current.logged? - projects = User.current.projects.active.select(:id, :name, :identifier, :lft, :rgt).to_a - if projects.any? - options = - ("" + - '').html_safe - - options << project_tree_options_for_select(projects, :selected => @project) do |p| - { :value => project_path(:id => p, :jump => current_menu_item) } - end - - content_tag( :span, nil, :class => 'jump-box-arrow') + - select_tag('project_quick_jump_box', options, :onchange => 'if (this.value != \'\') { window.location = this.value; }') + projects = projects_for_jump_box(User.current) + if @project && @project.persisted? + text = @project.name_was end + text ||= l(:label_jump_to_a_project) + url = autocomplete_projects_path(:format => 'js', :jump => current_menu_item) + + trigger = content_tag('span', text, :class => 'drdn-trigger') + q = text_field_tag('q', '', :id => 'projects-quick-search', :class => 'autocomplete', :data => {:automcomplete_url => url}, :autocomplete => 'off') + all = link_to(l(:label_project_all), projects_path(:jump => current_menu_item), :class => (@project.nil? && controller.class.main_menu ? 'selected' : nil)) + content = content_tag('div', + content_tag('div', q, :class => 'quick-search') + + content_tag('div', render_projects_for_jump_box(projects, @project), :class => 'drdn-items projects selection') + + content_tag('div', all, :class => 'drdn-items all-projects selection'), + :class => 'drdn-content' + ) + + content_tag('div', trigger + content, :id => "project-jump", :class => "drdn") end def project_tree_options_for_select(projects, options = {}) @@ -372,8 +423,8 @@ module ApplicationHelper # Yields the given block for each project with its level in the tree # # Wrapper for Project#project_tree - def project_tree(projects, &block) - Project.project_tree(projects, &block) + def project_tree(projects, options={}, &block) + Project.project_tree(projects, options, &block) end def principals_check_box_tags(name, principals) @@ -424,7 +475,7 @@ module ApplicationHelper end def html_hours(text) - text.gsub(%r{(\d+)\.(\d+)}, '\1.\2').html_safe + text.gsub(%r{(\d+)([\.:])(\d+)}, '\1\2\3').html_safe end def authoring(created, author, options={}) @@ -441,9 +492,7 @@ module ApplicationHelper end def syntax_highlight_lines(name, content) - lines = [] - syntax_highlight(name, content).each_line { |line| lines << line } - lines + syntax_highlight(name, content).each_line.to_a end def syntax_highlight(name, content) @@ -562,6 +611,9 @@ module ApplicationHelper css << 'project-' + @project.identifier if @project && @project.identifier.present? css << 'controller-' + controller_name css << 'action-' + action_name + if UserPreference::TEXTAREA_FONT_OPTIONS.include?(User.current.pref.textarea_font) + css << "textarea-#{User.current.pref.textarea_font}" + end css.join(' ') end @@ -596,7 +648,13 @@ module ApplicationHelper text = text.dup macros = catch_macros(text) - text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr) + + if options[:formatting] == false + text = h(text) + else + formatting = Setting.text_formatting + text = Redmine::WikiFormatting.to_html(formatting, text, :object => obj, :attribute => attr) + end @parsed_headings = [] @heading_anchors = {} @@ -604,7 +662,7 @@ module ApplicationHelper parse_sections(text, project, obj, attr, only_path, options) text = parse_non_pre_blocks(text, obj, macros) do |text| - [:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links].each do |method_name| + [:parse_inline_attachments, :parse_hires_images, :parse_wiki_links, :parse_redmine_links].each do |method_name| send method_name, text, project, obj, attr, only_path, options end end @@ -649,6 +707,15 @@ module ApplicationHelper parsed end + # add srcset attribute to img tags if filename includes @2x, @3x, etc. + # to support hires displays + def parse_hires_images(text, project, obj, attr, only_path, options) + text.gsub!(/src="([^"]+@(\dx)\.(bmp|gif|jpg|jpe|jpeg|png))"/i) do |m| + filename, dpr = $1, $2 + m + " srcset=\"#{filename} #{dpr}\"" + end + end + def parse_inline_attachments(text, project, obj, attr, only_path, options) return if options[:inline_attachments] == false @@ -751,11 +818,23 @@ module ApplicationHelper # source:some/file#L120 -> Link to line 120 of the file # source:some/file@52#L120 -> Link to line 120 of the file's revision 52 # export:some/file -> Force the download of the file + # Forums: + # forum#1 -> Link to forum with id 1 + # forum:Support -> Link to forum named "Support" + # forum:"Technical Support" -> Link to forum named "Technical Support" # Forum messages: # message#1218 -> Link to message with id 1218 - # Projects: + # Projects: # project:someproject -> Link to project named "someproject" # project#3 -> Link to project with id 3 + # News: + # news#2 -> Link to news item with id 1 + # news:Greetings -> Link to news item named "Greetings" + # news:"First Release" -> Link to news item named "First Release" + # Users: + # user:jsmith -> Link to user with login jsmith + # @jsmith -> Link to user with login jsmith + # user#2 -> Link to user with id 2 # # Links can refer other objects from other projects, using project identifier: # identifier:r52 @@ -763,8 +842,20 @@ module ApplicationHelper # identifier:version:1.0.0 # identifier:source:some/file def parse_redmine_links(text, default_project, obj, attr, only_path, options) - text.gsub!(%r{]+?)?>(.*?)|([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-_]+):)?(attachment|document|version|forum|news|message|project|commit|source|export)?(((#)|((([a-z0-9\-_]+)\|)?(r)))((\d+)((#note)?-(\d+))?)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]][^A-Za-z0-9_/])|,|\s|\]|<|$)}) do |m| - tag_content, leading, esc, project_prefix, project_identifier, prefix, repo_prefix, repo_identifier, sep, identifier, comment_suffix, comment_id = $2, $3, $4, $5, $6, $7, $12, $13, $10 || $14 || $20, $16 || $21, $17, $19 + text.gsub!(LINKS_RE) do |_| + tag_content = $~[:tag_content] + leading = $~[:leading] + esc = $~[:esc] + project_prefix = $~[:project_prefix] + project_identifier = $~[:project_identifier] + prefix = $~[:prefix] + repo_prefix = $~[:repo_prefix] + repo_identifier = $~[:repo_identifier] + sep = $~[:sep1] || $~[:sep2] || $~[:sep3] || $~[:sep4] + identifier = $~[:identifier1] || $~[:identifier2] || $~[:identifier3] + comment_suffix = $~[:comment_suffix] + comment_id = $~[:comment_id] + if tag_content $& else @@ -831,11 +922,12 @@ module ApplicationHelper if p = Project.visible.find_by_id(oid) link = link_to_project(p, {:only_path => only_path}, :class => 'project') end + when 'user' + u = User.visible.where(:id => oid, :type => 'User').first + link = link_to_user(u) if u end elsif sep == ':' - # removes the double quotes if any - name = identifier.gsub(%r{^"(.*)"$}, "\\1") - name = CGI.unescapeHTML(name) + name = remove_double_quotes(identifier) case prefix when 'document' if project && document = project.documents.visible.find_by_title(name) @@ -885,13 +977,20 @@ module ApplicationHelper attachments = options[:attachments] || [] attachments += obj.attachments if obj.respond_to?(:attachments) if attachments && attachment = Attachment.latest_attach(attachments, name) - link = link_to_attachment(attachment, :only_path => only_path, :download => true, :class => 'attachment') + link = link_to_attachment(attachment, :only_path => only_path, :class => 'attachment') end when 'project' if p = Project.visible.where("identifier = :s OR LOWER(name) = :s", :s => name.downcase).first link = link_to_project(p, {:only_path => only_path}, :class => 'project') end + when 'user' + u = User.visible.where(:login => name, :type => 'User').first + link = link_to_user(u) if u end + elsif sep == "@" + name = remove_double_quotes(identifier) + u = User.visible.where(:login => name, :type => 'User').first + link = link_to_user(u) if u end end (leading + (link || "#{project_prefix}#{prefix}#{repo_prefix}#{sep}#{identifier}#{comment_suffix}")) @@ -899,6 +998,45 @@ module ApplicationHelper end end + LINKS_RE = + %r{ + ]+?)?>(?.*?)| + (?[\s\(,\-\[\>]|^) + (?!)? + (?(?[a-z0-9\-_]+):)? + (?attachment|document|version|forum|news|message|project|commit|source|export|user)? + ( + ( + (?\#)| + ( + (?(?[a-z0-9\-_]+)\|)? + (?r) + ) + ) + ( + (?\d+) + (? + (\#note)? + -(?\d+) + )? + )| + ( + (?:) + (?[^"\s<>][^\s<>]*?|"[^"]+?") + )| + ( + (?@) + (?[a-z0-9_\-@\.]*) + ) + ) + (?= + (?=[[:punct:]][^A-Za-z0-9_/])| + ,| + \s| + \]| + <| + $) + }x HEADING_RE = /(]+)?>(.+?)<\/h(\d)>)/i unless const_defined?(:HEADING_RE) def parse_sections(text, project, obj, attr, only_path, options) @@ -1007,7 +1145,7 @@ module ApplicationHelper div_class = 'toc' div_class << ' right' if right_align div_class << ' left' if left_align - out = "
  • " + out = "
    • #{l :label_table_of_contents}
    • " root = headings.map(&:first).min current = root started = false @@ -1110,6 +1248,11 @@ module ApplicationHelper url = params[:back_url] if url.nil? && referer = request.env['HTTP_REFERER'] url = CGI.unescape(referer.to_s) + # URLs that contains the utf8=[checkmark] parameter added by Rails are + # parsed as invalid by URI.parse so the redirect to the back URL would + # not be accepted (ApplicationController#validate_back_url would return + # false) + url.gsub!(/(\?|&)utf8=\u2713&?/, '\1') end url end @@ -1129,7 +1272,7 @@ module ApplicationHelper link_to_function '', "toggleCheckboxesBySelector('#{selector}')", :title => "#{l(:button_check_all)} / #{l(:button_uncheck_all)}", - :class => 'toggle-checkboxes' + :class => 'icon icon-checked' end def progress_bar(pcts, options={}) @@ -1155,7 +1298,7 @@ module ApplicationHelper end end - def context_menu(url) + def context_menu unless @context_menu_included content_for :header_tags do javascript_include_tag('context_menu') + @@ -1168,7 +1311,7 @@ module ApplicationHelper end @context_menu_included = true end - javascript_tag "contextMenuInit('#{ url_for(url) }')" + nil end def calendar_for(field_id) @@ -1371,7 +1514,9 @@ module ApplicationHelper return self end - def link_to_content_update(text, url_params = {}, html_options = {}) - link_to(text, url_params, html_options) + # remove double quotes if any + def remove_double_quotes(identifier) + name = identifier.gsub(%r{^"(.*)"$}, "\\1") + return CGI.unescapeHTML(name) end end diff --git a/app/helpers/attachments_helper.rb b/app/helpers/attachments_helper.rb index f3969f4b4..36cf0d4fe 100644 --- a/app/helpers/attachments_helper.rb +++ b/app/helpers/attachments_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -34,7 +34,12 @@ module AttachmentsHelper def link_to_attachments(container, options = {}) options.assert_valid_keys(:author, :thumbnails) - attachments = container.attachments.preload(:author).to_a + attachments = if container.attachments.loaded? + container.attachments + else + container.attachments.preload(:author).to_a + end + if attachments.any? options = { :editable => container.attachments_editable?, @@ -51,19 +56,26 @@ module AttachmentsHelper end end - def render_api_attachment(attachment, api) + def render_api_attachment(attachment, api, options={}) api.attachment do - api.id attachment.id - api.filename attachment.filename - api.filesize attachment.filesize - api.content_type attachment.content_type - api.description attachment.description - api.content_url download_named_attachment_url(attachment, attachment.filename) - if attachment.thumbnailable? - api.thumbnail_url thumbnail_url(attachment) - end - api.author(:id => attachment.author.id, :name => attachment.author.name) if attachment.author - api.created_on attachment.created_on + render_api_attachment_attributes(attachment, api) + options.each { |key, value| eval("api.#{key} value") } end end + + def render_api_attachment_attributes(attachment, api) + api.id attachment.id + api.filename attachment.filename + api.filesize attachment.filesize + api.content_type attachment.content_type + api.description attachment.description + api.content_url download_named_attachment_url(attachment, attachment.filename) + if attachment.thumbnailable? + api.thumbnail_url thumbnail_url(attachment) + end + if attachment.author + api.author(:id => attachment.author.id, :name => attachment.author.name) + end + api.created_on attachment.created_on + end end diff --git a/app/helpers/auth_sources_helper.rb b/app/helpers/auth_sources_helper.rb index 8c8295a85..4f4b1cd52 100644 --- a/app/helpers/auth_sources_helper.rb +++ b/app/helpers/auth_sources_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb index 78a17fe93..0931dd048 100644 --- a/app/helpers/boards_helper.rb +++ b/app/helpers/boards_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/calendars_helper.rb b/app/helpers/calendars_helper.rb index cf624d2a8..715667c15 100644 --- a/app/helpers/calendars_helper.rb +++ b/app/helpers/calendars_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -53,6 +53,6 @@ module CalendarsHelper end def link_to_month(link_name, year, month, options={}) - link_to_content_update(h(link_name), params.merge(:year => year, :month => month), options) + link_to(link_name, {:params => request.query_parameters.merge(:year => year, :month => month)}, options) end end diff --git a/app/helpers/context_menus_helper.rb b/app/helpers/context_menus_helper.rb index e3cf728a6..0254d3add 100644 --- a/app/helpers/context_menus_helper.rb +++ b/app/helpers/context_menus_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/custom_fields_helper.rb b/app/helpers/custom_fields_helper.rb index 617a6123d..08c8c5887 100644 --- a/app/helpers/custom_fields_helper.rb +++ b/app/helpers/custom_fields_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/documents_helper.rb b/app/helpers/documents_helper.rb index 34ab8aec7..a6741b8cd 100644 --- a/app/helpers/documents_helper.rb +++ b/app/helpers/documents_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/email_addresses_helper.rb b/app/helpers/email_addresses_helper.rb index 81449edfa..1f44f212d 100644 --- a/app/helpers/email_addresses_helper.rb +++ b/app/helpers/email_addresses_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/enumerations_helper.rb b/app/helpers/enumerations_helper.rb index 737d5a513..2759e8457 100644 --- a/app/helpers/enumerations_helper.rb +++ b/app/helpers/enumerations_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/gantt_helper.rb b/app/helpers/gantt_helper.rb index 77d3b8364..65585898f 100644 --- a/app/helpers/gantt_helper.rb +++ b/app/helpers/gantt_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -23,8 +23,8 @@ module GanttHelper case in_or_out when :in if gantt.zoom < 4 - link_to_content_update l(:text_zoom_in), - params.merge(gantt.params.merge(:zoom => (gantt.zoom + 1))), + link_to l(:text_zoom_in), + {:params => request.query_parameters.merge(gantt.params.merge(:zoom => (gantt.zoom + 1)))}, :class => 'icon icon-zoom-in' else content_tag(:span, l(:text_zoom_in), :class => 'icon icon-zoom-in').html_safe @@ -32,8 +32,8 @@ module GanttHelper when :out if gantt.zoom > 1 - link_to_content_update l(:text_zoom_out), - params.merge(gantt.params.merge(:zoom => (gantt.zoom - 1))), + link_to l(:text_zoom_out), + {:params => request.query_parameters.merge(gantt.params.merge(:zoom => (gantt.zoom - 1)))}, :class => 'icon icon-zoom-out' else content_tag(:span, l(:text_zoom_out), :class => 'icon icon-zoom-out').html_safe diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index d321a29ef..7eb885f8a 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/imports_helper.rb b/app/helpers/imports_helper.rb index edbf2d13e..39f3b95ab 100644 --- a/app/helpers/imports_helper.rb +++ b/app/helpers/imports_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/issue_categories_helper.rb b/app/helpers/issue_categories_helper.rb index d214d5e3d..8af3452a5 100644 --- a/app/helpers/issue_categories_helper.rb +++ b/app/helpers/issue_categories_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/issue_relations_helper.rb b/app/helpers/issue_relations_helper.rb index d6bc7e446..6208e5600 100644 --- a/app/helpers/issue_relations_helper.rb +++ b/app/helpers/issue_relations_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/issue_statuses_helper.rb b/app/helpers/issue_statuses_helper.rb index cb466ae0e..49441dde5 100644 --- a/app/helpers/issue_statuses_helper.rb +++ b/app/helpers/issue_statuses_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index f2b00ccbf..181907e4d 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -32,29 +32,14 @@ module IssuesHelper end end - def grouped_issue_list(issues, query, issue_count_by_group, &block) - previous_group, first = false, true - totals_by_group = query.totalable_columns.inject({}) do |h, column| - h[column] = query.total_by_group_for(column) - h - end - issue_list(issues) do |issue, level| - group_name = group_count = nil - if query.grouped? - group = query.group_by_column.value(issue) - if first || group != previous_group - if group.blank? && group != false - group_name = "(#{l(:label_blank_value)})" - else - group_name = format_object(group) - end - group_name ||= "" - group_count = issue_count_by_group[group] - group_totals = totals_by_group.map {|column, t| total_tag(column, t[group] || 0)}.join(" ").html_safe - end + def grouped_issue_list(issues, query, &block) + ancestors = [] + grouped_query_results(issues, query) do |issue, group_name, group_count, group_totals| + while (ancestors.any? && !issue.is_descendant_of?(ancestors.last)) + ancestors.pop end - yield issue, level, group_name, group_count, group_totals - previous_group, first = group, false + yield issue, ancestors.size, group_name, group_count, group_totals + ancestors << issue unless issue.leaf? end end @@ -105,7 +90,7 @@ module IssuesHelper end def render_descendants_tree(issue) - s = '
      ' + s = '
      ' issue_list(issue.descendants.visible.preload(:status, :priority, :tracker, :assigned_to).sort_by(&:lft)) do |child, level| css = "issue issue-#{child.id} hascontextmenu #{child.css_classes}" css << " idnt idnt-#{level}" if level > 0 @@ -117,10 +102,42 @@ module IssuesHelper content_tag('td', child.disabled_core_fields.include?('done_ratio') ? '' : progress_bar(child.done_ratio), :class=> 'done_ratio'), :class => css) end - s << '
      ' + s << '' s.html_safe end + # Renders the list of related issues on the issue details view + def render_issue_relations(issue, relations) + manage_relations = User.current.allowed_to?(:manage_issue_relations, issue.project) + + s = ''.html_safe + relations.each do |relation| + other_issue = relation.other_issue(issue) + css = "issue hascontextmenu #{other_issue.css_classes}" + link = manage_relations ? link_to(l(:label_relation_delete), + relation_path(relation), + :remote => true, + :method => :delete, + :data => {:confirm => l(:text_are_you_sure)}, + :title => l(:label_relation_delete), + :class => 'icon-only icon-link-break' + ) : nil + + s << content_tag('tr', + 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', other_issue.status, :class => 'status') + + content_tag('td', format_date(other_issue.start_date), :class => 'start_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', link, :class => 'buttons'), + :id => "relation-#{relation.id}", + :class => css) + end + + content_tag('table', s, :class => 'list issues odd-even') + end + def issue_estimated_hours_details(issue) if issue.total_estimated_hours.present? if issue.total_estimated_hours == issue.estimated_hours @@ -135,11 +152,13 @@ module IssuesHelper def issue_spent_hours_details(issue) if issue.total_spent_hours > 0 + path = project_time_entries_path(issue.project, :issue_id => "~#{issue.id}") + if issue.total_spent_hours == issue.spent_hours - link_to(l_hours_short(issue.spent_hours), issue_time_entries_path(issue)) + link_to(l_hours_short(issue.spent_hours), path) else s = issue.spent_hours > 0 ? l_hours_short(issue.spent_hours) : "" - s << " (#{l(:label_total)}: #{link_to l_hours_short(issue.total_spent_hours), issue_time_entries_path(issue)})" + s << " (#{l(:label_total)}: #{link_to l_hours_short(issue.total_spent_hours), path})" s.html_safe end end @@ -165,7 +184,7 @@ module IssuesHelper :parent_issue_id => issue } attrs[:tracker_id] = issue.tracker unless issue.tracker.disabled_core_fields.include?('parent_issue_id') - link_to(l(:button_add), new_project_issue_path(issue.project, :issue => attrs)) + link_to(l(:button_add), new_project_issue_path(issue.project, :issue => attrs, :back_url => issue_path(issue))) end def trackers_options_for_select(issue) @@ -220,19 +239,45 @@ module IssuesHelper r.to_html end - def render_custom_fields_rows(issue) - values = issue.visible_custom_field_values + def render_half_width_custom_fields_rows(issue) + values = issue.visible_custom_field_values.reject {|value| value.custom_field.full_width_layout?} return if values.empty? half = (values.size / 2.0).ceil issue_fields_rows do |rows| values.each_with_index do |value, i| 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) - 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 + def render_full_width_custom_fields_rows(issue) + values = issue.visible_custom_field_values.select {|value| value.custom_field.full_width_layout?} + return if values.empty? + + s = ''.html_safe + values.each_with_index do |value, i| + attr_value = show_value(value) + next if attr_value.blank? + + if value.custom_field.text_formatting == 'full' + attr_value = content_tag('div', attr_value, class: 'wiki') + end + + content = + content_tag('hr') + + content_tag('p', content_tag('strong', custom_field_name_tag(value.custom_field) )) + + content_tag('div', attr_value, class: 'value') + s << content_tag('div', content, class: "cf_#{value.custom_field.id} attribute") + end + s + end + # Returns the path for updating the issue form # with project as the current project def update_issue_form_path(project, issue) @@ -269,64 +314,38 @@ module IssuesHelper # Returns an array of users that are proposed as watchers # on the new issue form 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 users = (users + issue.project.users.sort).uniq end users end - def sidebar_queries - unless @sidebar_queries - @sidebar_queries = IssueQuery.visible. - order("#{Query.table_name}.name ASC"). - # Project specific queries and global queries - where(@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id]). - to_a - end - @sidebar_queries - end - - def query_links(title, queries) - return '' if queries.empty? - # links to #index on issues/show - url_params = controller_name == 'issues' ? {:controller => 'issues', :action => 'index', :project_id => @project} : params - - content_tag('h3', title) + "\n" + - content_tag('ul', - queries.collect {|query| - css = 'query' - css << ' selected' if query == @query - content_tag('li', link_to(query.name, url_params.merge(:query_id => query), :class => css)) - }.join("\n").html_safe, - :class => 'queries' - ) + "\n" - end - - def render_sidebar_queries - out = ''.html_safe - out << query_links(l(:label_my_queries), sidebar_queries.select(&:is_private?)) - out << query_links(l(:label_query_plural), sidebar_queries.reject(&:is_private?)) - out - end - - def email_issue_attributes(issue, user) + def email_issue_attributes(issue, user, html) items = [] %w(author status priority assigned_to category fixed_version).each do |attribute| unless issue.disabled_core_fields.include?(attribute+"_id") - items << "#{l("field_#{attribute}")}: #{issue.send attribute}" + if html + items << content_tag('strong', "#{l("field_#{attribute}")}: ") + (issue.send attribute) + else + items << "#{l("field_#{attribute}")}: #{issue.send attribute}" + end end end issue.visible_custom_field_values(user).each do |value| - items << "#{value.custom_field.name}: #{show_value(value, false)}" + if html + items << content_tag('strong', "#{value.custom_field.name}: ") + show_value(value, false) + else + items << "#{value.custom_field.name}: #{show_value(value, false)}" + end end items end def render_email_issue_attributes(issue, user, html=false) - items = email_issue_attributes(issue, user) + items = email_issue_attributes(issue, user, html) if html - content_tag('ul', items.map{|s| content_tag('li', s)}.join("\n").html_safe) + content_tag('ul', items.map{|s| content_tag('li', s)}.join("\n").html_safe, :class => "details") else items.map{|s| "* #{s}"}.join("\n") end @@ -376,6 +395,7 @@ module IssuesHelper def show_detail(detail, no_html=false, options={}) multiple = false show_diff = false + no_details = false case detail.property when 'attr' @@ -411,7 +431,9 @@ module IssuesHelper custom_field = detail.custom_field if custom_field label = custom_field.name - if custom_field.format.class.change_as_diff + if custom_field.format.class.change_no_details + no_details = true + elsif custom_field.format.class.change_as_diff show_diff = true else multiple = custom_field.multiple? @@ -450,21 +472,19 @@ module IssuesHelper if detail.property == 'attachment' && value.present? && atta = detail.journal.journalized.attachments.detect {|a| a.id == detail.prop_key.to_i} # Link to the attachment if it has not been removed - value = link_to_attachment(atta, :download => true, :only_path => options[:only_path]) - if options[:only_path] != false && (atta.is_text? || atta.is_image?) + value = link_to_attachment(atta, only_path: options[:only_path]) + if options[:only_path] != false value += ' ' - value += link_to(l(:button_view), - { :controller => 'attachments', :action => 'show', - :id => atta, :filename => atta.filename }, - :class => 'icon-only icon-magnifier', - :title => l(:button_view)) + value += link_to_attachment atta, class: 'icon-only icon-download', title: l(:button_download), download: true end else value = content_tag("i", h(value)) if value end end - if show_diff + if no_details + s = l(:text_journal_changed_no_detail, :label => label).html_safe + elsif show_diff s = l(:text_journal_changed_no_detail, :label => label) unless no_html diff_link = link_to 'diff', diff --git a/app/helpers/journals_helper.rb b/app/helpers/journals_helper.rb index f9dc7c9ea..b1d8774c4 100644 --- a/app/helpers/journals_helper.rb +++ b/app/helpers/journals_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -27,9 +27,9 @@ module JournalsHelper def render_notes(issue, journal, options={}) content = '' - editable = User.current.logged? && (User.current.allowed_to?(:edit_issue_notes, issue.project) || (journal.user == User.current && User.current.allowed_to?(:edit_own_issue_notes, issue.project))) + css_classes = "wiki" links = [] - if !journal.notes.blank? + if journal.notes.present? links << link_to(l(:button_quote), quoted_issue_path(issue, :journal_id => journal), :remote => true, @@ -37,25 +37,33 @@ module JournalsHelper :title => l(:button_quote), :class => 'icon-only icon-comment' ) if options[:reply_links] - links << link_to(l(:button_edit), - edit_journal_path(journal), - :remote => true, - :method => 'get', - :title => l(:button_edit), - :class => 'icon-only icon-edit' - ) if editable - links << link_to(l(:button_delete), - journal_path(journal, :notes => ""), - :remote => true, - :method => 'put', :data => {:confirm => l(:text_are_you_sure)}, - :title => l(:button_delete), - :class => 'icon-only icon-del' - ) if editable + + if journal.editable_by?(User.current) + links << link_to(l(:button_edit), + edit_journal_path(journal), + :remote => true, + :method => 'get', + :title => l(:button_edit), + :class => 'icon-only icon-edit' + ) + links << link_to(l(:button_delete), + journal_path(journal, :journal => {:notes => ""}), + :remote => true, + :method => 'put', :data => {:confirm => l(:text_are_you_sure)}, + :title => l(:button_delete), + :class => 'icon-only icon-del' + ) + css_classes << " editable" + end end content << content_tag('div', links.join(' ').html_safe, :class => 'contextual') unless links.empty? content << textilizable(journal, :notes) - css_classes = "wiki" - css_classes << " editable" if editable content_tag('div', content.html_safe, :id => "journal-#{journal.id}-notes", :class => css_classes) end + + def render_private_notes_indicator(journal) + content = journal.private_notes? ? l(:field_is_private) : '' + css_classes = journal.private_notes? ? 'private' : '' + content_tag('span', content.html_safe, :id => "journal-#{journal.id}-private_notes", :class => css_classes) + end end diff --git a/app/helpers/mail_handler_helper.rb b/app/helpers/mail_handler_helper.rb index 64311b56f..f7957cea5 100644 --- a/app/helpers/mail_handler_helper.rb +++ b/app/helpers/mail_handler_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/members_helper.rb b/app/helpers/members_helper.rb index f28222383..ff7ff1bff 100644 --- a/app/helpers/members_helper.rb +++ b/app/helpers/members_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/messages_helper.rb b/app/helpers/messages_helper.rb index a3d1fa318..77ca38526 100644 --- a/app/helpers/messages_helper.rb +++ b/app/helpers/messages_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/my_helper.rb b/app/helpers/my_helper.rb index 4942d7e04..6c61de894 100644 --- a/app/helpers/my_helper.rb +++ b/app/helpers/my_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -18,58 +18,150 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. module MyHelper - def calendar_items(startdt, enddt) - Issue.visible. + # Renders the blocks + def render_blocks(blocks, user, options={}) + s = ''.html_safe + + if blocks.present? + blocks.each do |block| + s << render_block(block, user).to_s + end + end + s + end + + # Renders a single block + def render_block(block, user) + content = render_block_content(block, user) + if content.present? + handle = content_tag('span', '', :class => 'sort-handle', :title => l(:button_move)) + close = link_to(l(:button_delete), + {:action => "remove_block", :block => block}, + :remote => true, :method => 'post', + :class => "icon-only icon-close", :title => l(:button_delete)) + content = content_tag('div', handle + close, :class => 'contextual') + content + + content_tag('div', content, :class => "mypage-box", :id => "block-#{block}") + end + end + + # Renders a single block content + def render_block_content(block, user) + unless block_definition = Redmine::MyPage.find_block(block) + Rails.logger.warn("Unknown block \"#{block}\" found in #{user.login} (id=#{user.id}) preferences") + return + end + + settings = user.pref.my_page_settings(block) + if partial = block_definition[:partial] + begin + render(:partial => partial, :locals => {:user => user, :settings => settings, :block => block}) + rescue ActionView::MissingTemplate + Rails.logger.warn("Partial \"#{partial}\" missing for block \"#{block}\" found in #{user.login} (id=#{user.id}) preferences") + return nil + end + else + send "render_#{block_definition[:name]}_block", block, settings + end + end + + # Returns the select tag used to add a block to My page + def block_select_tag(user) + blocks_in_use = user.pref.my_page_layout.values.flatten + options = content_tag('option') + Redmine::MyPage.block_options(blocks_in_use).each do |label, block| + options << content_tag('option', label, :value => block, :disabled => block.blank?) + end + select_tag('block', options, :id => "block-select", :onchange => "$('#block-form').submit();") + end + + def render_calendar_block(block, settings) + calendar = Redmine::Helpers::Calendar.new(User.current.today, current_language, :week) + calendar.events = Issue.visible. where(:project_id => User.current.projects.map(&:id)). - where("(start_date>=? and start_date<=?) or (due_date>=? and due_date<=?)", startdt, enddt, startdt, enddt). + where("(start_date>=? and start_date<=?) or (due_date>=? and due_date<=?)", calendar.startdt, calendar.enddt, calendar.startdt, calendar.enddt). includes(:project, :tracker, :priority, :assigned_to). references(:project, :tracker, :priority, :assigned_to). to_a + + render :partial => 'my/blocks/calendar', :locals => {:calendar => calendar, :block => block} end - def documents_items - Document.visible.order("#{Document.table_name}.created_on DESC").limit(10).to_a + def render_documents_block(block, settings) + documents = Document.visible.order("#{Document.table_name}.created_on DESC").limit(10).to_a + + render :partial => 'my/blocks/documents', :locals => {:block => block, :documents => documents} end - def issuesassignedtome_items - Issue.visible.open. - assigned_to(User.current). - limit(10). - includes(:status, :project, :tracker, :priority). - references(:status, :project, :tracker, :priority). - order("#{IssuePriority.table_name}.position DESC, #{Issue.table_name}.updated_on DESC") + def render_issuesassignedtome_block(block, settings) + query = IssueQuery.new(:name => l(:label_assigned_to_me_issues), :user => User.current) + query.add_filter 'assigned_to_id', '=', ['me'] + query.column_names = settings[:columns].presence || ['project', 'tracker', 'status', 'subject'] + query.sort_criteria = settings[:sort].presence || [['priority', 'desc'], ['updated_on', 'desc']] + issues = query.issues(:limit => 10) + + render :partial => 'my/blocks/issues', :locals => {:query => query, :issues => issues, :block => block} end - def issuesreportedbyme_items - Issue.visible.open. - where(:author_id => User.current.id). - limit(10). - includes(:status, :project, :tracker). - references(:status, :project, :tracker). - order("#{Issue.table_name}.updated_on DESC") + def render_issuesreportedbyme_block(block, settings) + query = IssueQuery.new(:name => l(:label_reported_issues), :user => User.current) + query.add_filter 'author_id', '=', ['me'] + query.column_names = settings[:columns].presence || ['project', 'tracker', 'status', 'subject'] + query.sort_criteria = settings[:sort].presence || [['updated_on', 'desc']] + issues = query.issues(:limit => 10) + + render :partial => 'my/blocks/issues', :locals => {:query => query, :issues => issues, :block => block} end - def issueswatched_items - Issue.visible.open.on_active_project.watched_by(User.current.id).recently_updated.limit(10) + def render_issueswatched_block(block, settings) + query = IssueQuery.new(:name => l(:label_watched_issues), :user => User.current) + query.add_filter 'watcher_id', '=', ['me'] + query.column_names = settings[:columns].presence || ['project', 'tracker', 'status', 'subject'] + query.sort_criteria = settings[:sort].presence || [['updated_on', 'desc']] + issues = query.issues(:limit => 10) + + render :partial => 'my/blocks/issues', :locals => {:query => query, :issues => issues, :block => block} end - def news_items - News.visible. + def render_issuequery_block(block, settings) + query = IssueQuery.visible.find_by_id(settings[:query_id]) + + if query + query.column_names = settings[:columns] if settings[:columns].present? + query.sort_criteria = settings[:sort] if settings[:sort].present? + issues = query.issues(:limit => 10) + render :partial => 'my/blocks/issues', :locals => {:query => query, :issues => issues, :block => block, :settings => settings} + else + queries = IssueQuery.visible.sorted + render :partial => 'my/blocks/issue_query_selection', :locals => {:queries => queries, :block => block, :settings => settings} + end + end + + def render_news_block(block, settings) + news = News.visible. where(:project_id => User.current.projects.map(&:id)). limit(10). includes(:project, :author). references(:project, :author). order("#{News.table_name}.created_on DESC"). to_a + + render :partial => 'my/blocks/news', :locals => {:block => block, :news => news} end - def timelog_items - TimeEntry. - where("#{TimeEntry.table_name}.user_id = ? AND #{TimeEntry.table_name}.spent_on BETWEEN ? AND ?", User.current.id, User.current.today - 6, User.current.today). + def render_timelog_block(block, settings) + days = settings[:days].to_i + days = 7 if days < 1 || days > 365 + + entries = TimeEntry. + where("#{TimeEntry.table_name}.user_id = ? AND #{TimeEntry.table_name}.spent_on BETWEEN ? AND ?", User.current.id, User.current.today - (days - 1), User.current.today). joins(:activity, :project). references(:issue => [:tracker, :status]). includes(:issue => [:tracker, :status]). order("#{TimeEntry.table_name}.spent_on DESC, #{Project.table_name}.name ASC, #{Tracker.table_name}.position ASC, #{Issue.table_name}.id ASC"). to_a + entries_by_day = entries.group_by(&:spent_on) + + render :partial => 'my/blocks/timelog', :locals => {:block => block, :entries => entries, :entries_by_day => entries_by_day, :days => days} end end diff --git a/app/helpers/news_helper.rb b/app/helpers/news_helper.rb index 74d63e46a..f105593fa 100644 --- a/app/helpers/news_helper.rb +++ b/app/helpers/news_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/principal_memberships_helper.rb b/app/helpers/principal_memberships_helper.rb index e3ef37798..bf6898fde 100644 --- a/app/helpers/principal_memberships_helper.rb +++ b/app/helpers/principal_memberships_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -46,6 +46,14 @@ module PrincipalMembershipsHelper end end + def edit_principal_membership_path(principal, *args) + if principal.is_a?(Group) + edit_group_membership_path(principal, *args) + else + edit_user_membership_path(principal, *args) + end + end + def principal_membership_path(principal, membership, *args) if principal.is_a?(Group) group_membership_path(principal, membership, *args) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 6a47b1ebc..78e192b5a 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -22,7 +22,8 @@ module ProjectsHelper tabs = [{:name => 'info', :action => :edit_project, :partial => 'projects/edit', :label => :label_information_plural}, {:name => 'modules', :action => :select_project_modules, :partial => 'projects/settings/modules', :label => :label_module_plural}, {:name => 'members', :action => :manage_members, :partial => 'projects/settings/members', :label => :label_member_plural}, - {:name => 'versions', :action => :manage_versions, :partial => 'projects/settings/versions', :label => :label_version_plural}, + {:name => 'versions', :action => :manage_versions, :partial => 'projects/settings/versions', :label => :label_version_plural, + :url => {:tab => 'versions', :version_status => params[:version_status], :version_name => params[:version_name]}}, {:name => 'categories', :action => :manage_categories, :partial => 'projects/settings/issue_categories', :label => :label_issue_category_plural}, {:name => 'wiki', :action => :manage_wiki, :partial => 'projects/settings/wiki', :label => :label_wiki}, {:name => 'repositories', :action => :manage_repository, :partial => 'projects/settings/repositories', :label => :label_repository_plural}, @@ -47,24 +48,17 @@ module ProjectsHelper end def render_project_action_links - links = [] + links = "".html_safe if User.current.allowed_to?(:add_project, nil, :global => true) links << link_to(l(:label_project_new), new_project_path, :class => 'icon icon-add') end - if User.current.allowed_to?(:view_issues, nil, :global => true) - links << link_to(l(:label_issue_view_all), issues_path) - end - if User.current.allowed_to?(:view_time_entries, nil, :global => true) - links << link_to(l(:label_overall_spent_time), time_entries_path) - end - links << link_to(l(:label_overall_activity), activity_path) - links.join(" | ").html_safe + links end # Renders the projects index def render_project_hierarchy(projects) render_project_nested_lists(projects) do |project| - s = link_to_project(project, {}, :class => "#{project.css_classes} #{User.current.member_of?(project) ? 'my-project' : nil}") + s = link_to_project(project, {}, :class => "#{project.css_classes} #{User.current.member_of?(project) ? 'icon icon-fav my-project' : nil}") if project.description.present? s << content_tag('div', textilizable(project.short_description, :project => project), :class => 'wiki description') end @@ -95,6 +89,11 @@ module ProjectsHelper version_options_for_select(versions, project.default_version) end + def project_default_assigned_to_options(project) + assignable_users = (project.assignable_users.to_a + [project.default_assigned_to]).uniq.compact + principals_options_for_select(assignable_users, project.default_assigned_to) + end + def format_version_sharing(sharing) sharing = 'none' unless Version::VERSION_SHARINGS.include?(sharing) l("label_version_sharing_#{sharing}") @@ -126,11 +125,16 @@ module ProjectsHelper end end if include_in_api_response?('issue_categories') + api.array :time_entry_activities do + project.activities.each do |activity| + api.time_entry_activity(:id => activity.id, :name => activity.name) + end + end if include_in_api_response?('time_entry_activities') + api.array :enabled_modules do project.enabled_modules.each do |enabled_module| api.enabled_module(:id => enabled_module.id, :name => enabled_module.name) end end if include_in_api_response?('enabled_modules') - end end diff --git a/app/helpers/queries_helper.rb b/app/helpers/queries_helper.rb index 278643191..6ad8437e3 100644 --- a/app/helpers/queries_helper.rb +++ b/app/helpers/queries_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -24,11 +24,15 @@ module QueriesHelper ungrouped = [] grouped = {} query.available_filters.map do |field, field_options| - if [:tree, :relation].include?(field_options[:type]) + if field_options[:type] == :relation group = :label_relations + elsif field_options[:type] == :tree + group = query.is_a?(IssueQuery) ? :label_relations : nil + elsif field =~ /^cf_\d+\./ + group = (field_options[:through] || field_options[:field]).try(:name) elsif field =~ /^(.+)\./ # association filters - group = "field_#{$1}" + group = "field_#{$1}".to_sym elsif %w(member_of_group assigned_to_role).include?(field) group = :field_assigned_to elsif field_options[:type] == :date_past || field_options[:type] == :date @@ -41,12 +45,12 @@ module QueriesHelper end end # Don't group dates if there's only one (eg. time entries filters) - if grouped[:label_date].try(:size) == 1 + if grouped[:label_date].try(:size) == 1 ungrouped << grouped.delete(:label_date).first end s = options_for_select([[]] + ungrouped) if grouped.present? - localized_grouped = grouped.map {|k,v| [l(k), v]} + localized_grouped = grouped.map {|k,v| [k.is_a?(Symbol) ? l(k) : k.to_s, v]} s << grouped_options_for_select(localized_grouped) end s @@ -76,6 +80,11 @@ module QueriesHelper query_filters_hidden_tags(query) + query_columns_hidden_tags(query) end + def group_by_column_select_tag(query) + options = [[]] + query.groupable_columns.collect {|c| [c.caption, c.name.to_s]} + select_tag('group_by', options_for_select(options, @query.group_by)) + end + def available_block_columns_tags(query) tags = ''.html_safe query.available_block_columns.each do |column| @@ -106,6 +115,33 @@ module QueriesHelper render :partial => 'queries/columns', :locals => {:query => query, :tag_name => tag_name} end + def grouped_query_results(items, query, &block) + result_count_by_group = query.result_count_by_group + previous_group, first = false, true + totals_by_group = query.totalable_columns.inject({}) do |h, column| + h[column] = query.total_by_group_for(column) + h + end + items.each do |item| + group_name = group_count = nil + if query.grouped? + group = query.group_by_column.value(item) + if first || group != previous_group + if group.blank? && group != false + group_name = "(#{l(:label_blank_value)})" + else + group_name = format_object(group) + end + group_name ||= "" + group_count = result_count_by_group ? result_count_by_group[group] : nil + group_totals = totals_by_group.map {|column, t| total_tag(column, t[group] || 0)}.join(" ").html_safe + end + end + yield item, group_name, group_count, group_totals + previous_group, first = group, false + end + end + def render_query_totals(query) return unless query.totalable_columns.present? totals = query.totalable_columns.map do |column| @@ -116,83 +152,125 @@ module QueriesHelper def total_tag(column, value) label = content_tag('span', "#{column.caption}:") - value = content_tag('span', format_object(value), :class => 'value') + value = if [:hours, :spent_hours, :total_spent_hours, :estimated_hours].include? column.name + format_hours(value) + else + format_object(value) + end + value = content_tag('span', value, :class => 'value') content_tag('span', label + " " + value, :class => "total-for-#{column.name.to_s.dasherize}") end - def column_header(column) - column.sortable ? sort_header_tag(column.name.to_s, :caption => column.caption, - :default_order => column.default_order) : - content_tag('th', h(column.caption)) + def column_header(query, column, options={}) + if column.sortable? + css, order = nil, column.default_order + if column.name.to_s == query.sort_criteria.first_key + if query.sort_criteria.first_asc? + css = 'sort asc' + order = 'desc' + else + css = 'sort desc' + order = 'asc' + end + end + param_key = options[:sort_param] || :sort + sort_param = { param_key => query.sort_criteria.add(column.name, order).to_param } + while sort_param.keys.first.to_s =~ /^(.+)\[(.+)\]$/ + sort_param = {$1 => {$2 => sort_param.values.first}} + end + link_options = { + :title => l(:label_sort_by, "\"#{column.caption}\""), + :class => css + } + if options[:sort_link_options] + link_options.merge! options[:sort_link_options] + end + content = link_to(column.caption, + {:params => request.query_parameters.deep_merge(sort_param)}, + link_options + ) + else + content = column.caption + end + content_tag('th', content) end - def column_content(column, issue) - value = column.value_object(issue) + def column_content(column, item) + value = column.value_object(item) if value.is_a?(Array) - values = value.collect {|v| column_value(column, issue, v)}.compact + values = value.collect {|v| column_value(column, item, v)}.compact safe_join(values, ', ') else - column_value(column, issue, value) + column_value(column, item, value) end end - - def column_value(column, issue, value) + + def column_value(column, item, value) case column.name when :id - link_to value, issue_path(issue) + link_to value, issue_path(item) when :subject - link_to value, issue_path(issue) + link_to value, issue_path(item) when :parent value ? (value.visible? ? link_to_issue(value, :subject => false) : "##{value.id}") : '' when :description - issue.description? ? content_tag('div', textilizable(issue, :description), :class => "wiki") : '' + item.description? ? content_tag('div', textilizable(item, :description), :class => "wiki") : '' + when :last_notes + item.last_notes.present? ? content_tag('div', textilizable(item, :last_notes), :class => "wiki") : '' when :done_ratio progress_bar(value) when :relations content_tag('span', - value.to_s(issue) {|other| link_to_issue(other, :subject => false, :tracker => false)}.html_safe, - :class => value.css_classes_for(issue)) + value.to_s(item) {|other| link_to_issue(other, :subject => false, :tracker => false)}.html_safe, + :class => value.css_classes_for(item)) + when :hours, :estimated_hours + format_hours(value) + when :spent_hours + link_to_if(value > 0, format_hours(value), project_time_entries_path(item.project, :issue_id => "#{item.id}")) + when :total_spent_hours + link_to_if(value > 0, format_hours(value), project_time_entries_path(item.project, :issue_id => "~#{item.id}")) + when :attachments + value.to_a.map {|a| format_object(a)}.join(" ").html_safe else format_object(value) end end - def csv_content(column, issue) - value = column.value_object(issue) + def csv_content(column, item) + value = column.value_object(item) if value.is_a?(Array) - value.collect {|v| csv_value(column, issue, v)}.compact.join(', ') + value.collect {|v| csv_value(column, item, v)}.compact.join(', ') else - csv_value(column, issue, value) + csv_value(column, item, value) end end def csv_value(column, object, value) - format_object(value, false) do |value| - case value.class.name - when 'Float' - sprintf("%.2f", value).gsub('.', l(:general_csv_decimal_separator)) - when 'IssueRelation' - value.to_s(object) - when 'Issue' - if object.is_a?(TimeEntry) - "#{value.tracker} ##{value.id}: #{value.subject}" + case column.name + when :attachments + value.to_a.map {|a| a.filename}.join("\n") + else + format_object(value, false) do |value| + case value.class.name + when 'Float' + sprintf("%.2f", value).gsub('.', l(:general_csv_decimal_separator)) + when 'IssueRelation' + value.to_s(object) + when 'Issue' + if object.is_a?(TimeEntry) + "#{value.tracker} ##{value.id}: #{value.subject}" + else + value.id + end else - value.id + value end - else - value end end end def query_to_csv(items, query, options={}) - options ||= {} - columns = (options[:columns] == 'all' ? query.available_inline_columns : query.inline_columns) - query.available_block_columns.each do |column| - if options[column.name].present? - columns << column - end - end + columns = query.columns Redmine::Export::CSV.generate do |csv| # csv header fields @@ -205,40 +283,51 @@ module QueriesHelper end # Retrieve query from session or build a new query - def retrieve_query - if !params[:query_id].blank? + def retrieve_query(klass=IssueQuery, use_session=true) + session_key = klass.name.underscore.to_sym + + if params[:query_id].present? cond = "project_id IS NULL" cond << " OR project_id = #{@project.id}" if @project - @query = IssueQuery.where(cond).find(params[:query_id]) + @query = klass.where(cond).find(params[:query_id]) raise ::Unauthorized unless @query.visible? @query.project = @project - session[:query] = {:id => @query.id, :project_id => @query.project_id} - sort_clear - elsif api_request? || params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil) + session[session_key] = {:id => @query.id, :project_id => @query.project_id} if use_session + elsif api_request? || params[:set_filter] || !use_session || session[session_key].nil? || session[session_key][:project_id] != (@project ? @project.id : nil) # Give it a name, required to be valid - @query = IssueQuery.new(:name => "_") - @query.project = @project + @query = klass.new(:name => "_", :project => @project) @query.build_from_params(params) - session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names, :totalable_names => @query.totalable_names} + session[session_key] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names, :totalable_names => @query.totalable_names, :sort => @query.sort_criteria.to_a} if use_session else # retrieve from session @query = nil - @query = IssueQuery.find_by_id(session[:query][:id]) if session[:query][:id] - @query ||= IssueQuery.new(:name => "_", :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names], :totalable_names => session[:query][:totalable_names]) + @query = klass.find_by_id(session[session_key][:id]) if session[session_key][:id] + @query ||= klass.new(:name => "_", :filters => session[session_key][:filters], :group_by => session[session_key][:group_by], :column_names => session[session_key][:column_names], :totalable_names => session[session_key][:totalable_names], :sort_criteria => session[session_key][:sort]) @query.project = @project end + if params[:sort].present? + @query.sort_criteria = params[:sort] + if use_session + session[session_key] ||= {} + session[session_key][:sort] = @query.sort_criteria.to_a + end + end + @query end - def retrieve_query_from_session - if session[:query] - if session[:query][:id] - @query = IssueQuery.find_by_id(session[:query][:id]) + def retrieve_query_from_session(klass=IssueQuery) + session_key = klass.name.underscore.to_sym + session_data = session[session_key] + + if session_data + if session_data[:id] + @query = IssueQuery.find_by_id(session_data[:id]) return unless @query else - @query = IssueQuery.new(:name => "_", :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names], :totalable_names => session[:query][:totalable_names]) + @query = IssueQuery.new(:name => "_", :filters => session_data[:filters], :group_by => session_data[:group_by], :column_names => session_data[:column_names], :totalable_names => session_data[:totalable_names], :sort_criteria => session[session_key][:sort]) end - if session[:query].has_key?(:project_id) - @query.project_id = session[:query][:project_id] + if session_data.has_key?(:project_id) + @query.project_id = session_data[:project_id] else @query.project = @project end @@ -261,10 +350,8 @@ module QueriesHelper else tags << hidden_field_tag("f[]", "", :id => nil) end - if query.column_names.present? - query.column_names.each do |name| - tags << hidden_field_tag("c[]", name, :id => nil) - end + query.columns.each do |column| + tags << hidden_field_tag("c[]", column.name, :id => nil) end if query.totalable_names.present? query.totalable_names.each do |name| @@ -274,7 +361,46 @@ module QueriesHelper if query.group_by.present? tags << hidden_field_tag("group_by", query.group_by, :id => nil) end + if query.sort_criteria.present? + tags << hidden_field_tag("sort", query.sort_criteria.to_param, :id => nil) + end tags end + + def query_hidden_sort_tag(query) + hidden_field_tag("sort", query.sort_criteria.to_param, :id => nil) + end + + # Returns the queries that are rendered in the sidebar + def sidebar_queries(klass, project) + klass.visible.global_or_on_project(@project).sorted.to_a + end + + # Renders a group of queries + def query_links(title, queries) + return '' if queries.empty? + # links to #index on issues/show + url_params = controller_name == 'issues' ? {:controller => 'issues', :action => 'index', :project_id => @project} : {} + + content_tag('h3', title) + "\n" + + content_tag('ul', + queries.collect {|query| + css = 'query' + css << ' selected' if query == @query + content_tag('li', link_to(query.name, url_params.merge(:query_id => query), :class => css)) + }.join("\n").html_safe, + :class => 'queries' + ) + "\n" + end + + # Renders the list of queries for the sidebar + def render_sidebar_queries(klass, project) + queries = sidebar_queries(klass, project) + + out = ''.html_safe + out << query_links(l(:label_my_queries), queries.select(&:is_private?)) + out << query_links(l(:label_query_plural), queries.reject(&:is_private?)) + out + end end diff --git a/app/helpers/reports_helper.rb b/app/helpers/reports_helper.rb index 7638012a0..201c99024 100644 --- a/app/helpers/reports_helper.rb +++ b/app/helpers/reports_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb index 651535fbd..c2e07370f 100644 --- a/app/helpers/repositories_helper.rb +++ b/app/helpers/repositories_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -185,7 +185,7 @@ module RepositoriesHelper scm_path_info_tag(repository)) + scm_path_encoding_tag(form, repository) + content_tag('p', form.check_box( - :extra_report_last_commit, + :report_last_commit, :label => l(:label_git_report_last_commit) )) end diff --git a/app/helpers/roles_helper.rb b/app/helpers/roles_helper.rb index 737c11dc7..ce5ffe71b 100644 --- a/app/helpers/roles_helper.rb +++ b/app/helpers/roles_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/routes_helper.rb b/app/helpers/routes_helper.rb index b8415a510..c0a367563 100644 --- a/app/helpers/routes_helper.rb +++ b/app/helpers/routes_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -29,6 +29,14 @@ module RoutesHelper end end + def _project_news_path(project, *args) + if project + project_news_index_path(project, *args) + else + news_index_path(*args) + end + end + def _new_project_issue_path(project, *args) if project new_project_issue_path(project, *args) @@ -46,9 +54,7 @@ module RoutesHelper end def _time_entries_path(project, issue, *args) - if issue - issue_time_entries_path(issue, *args) - elsif project + if project project_time_entries_path(project, *args) else time_entries_path(*args) @@ -56,9 +62,7 @@ module RoutesHelper end def _report_time_entries_path(project, issue, *args) - if issue - report_issue_time_entries_path(issue, *args) - elsif project + if project report_project_time_entries_path(project, *args) else report_time_entries_path(*args) diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index 2554cf99f..8788711e7 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb index 2c657f0df..86cdff9e4 100644 --- a/app/helpers/settings_helper.rb +++ b/app/helpers/settings_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -25,6 +25,7 @@ module SettingsHelper {:name => 'api', :partial => 'settings/api', :label => :label_api}, {:name => 'projects', :partial => 'settings/projects', :label => :label_project_plural}, {:name => 'issues', :partial => 'settings/issues', :label => :label_issue_tracking}, + {:name => 'timelog', :partial => 'settings/timelog', :label => :label_time_tracking}, {:name => 'attachments', :partial => 'settings/attachments', :label => :label_attachment_plural}, {:name => 'notifications', :partial => 'settings/notifications', :label => :field_mail_notification}, {:name => 'mail_handler', :partial => 'settings/mail_handler', :label => :label_incoming_emails}, @@ -32,18 +33,35 @@ module SettingsHelper ] end + def render_settings_error(errors) + return if errors.blank? + s = ''.html_safe + errors.each do |name, message| + s << content_tag('li', content_tag('b', l("setting_#{name}")) + " " + message) + end + content_tag('div', content_tag('ul', s), :id => 'errorExplanation') + end + + def setting_value(setting) + value = nil + if params[:settings] + value = params[:settings][setting] + end + value || Setting.send(setting) + end + def setting_select(setting, choices, options={}) if blank_text = options.delete(:blank) choices = [[blank_text.is_a?(Symbol) ? l(blank_text) : blank_text, '']] + choices end setting_label(setting, options).html_safe + select_tag("settings[#{setting}]", - options_for_select(choices, Setting.send(setting).to_s), + options_for_select(choices, setting_value(setting).to_s), options).html_safe end def setting_multiselect(setting, choices, options={}) - setting_values = Setting.send(setting) + setting_values = setting_value(setting) setting_values = [] unless setting_values.is_a?(Array) content_tag("label", l(options[:label] || "setting_#{setting}")) + @@ -65,18 +83,18 @@ module SettingsHelper def setting_text_field(setting, options={}) setting_label(setting, options).html_safe + - text_field_tag("settings[#{setting}]", Setting.send(setting), options).html_safe + text_field_tag("settings[#{setting}]", setting_value(setting), options).html_safe end def setting_text_area(setting, options={}) setting_label(setting, options).html_safe + - text_area_tag("settings[#{setting}]", Setting.send(setting), options).html_safe + text_area_tag("settings[#{setting}]", setting_value(setting), options).html_safe end def setting_check_box(setting, options={}) setting_label(setting, options).html_safe + hidden_field_tag("settings[#{setting}]", 0, :id => nil).html_safe + - check_box_tag("settings[#{setting}]", 1, Setting.send("#{setting}?"), options).html_safe + check_box_tag("settings[#{setting}]", 1, setting_value(setting).to_s != '0', options).html_safe end def setting_label(setting, options={}) @@ -97,7 +115,7 @@ module SettingsHelper tag = check_box_tag('settings[notified_events][]', notifiable.name, - Setting.notified_events.include?(notifiable.name), + setting_value('notified_events').include?(notifiable.name), :id => nil, :data => tag_data) diff --git a/app/helpers/sort_helper.rb b/app/helpers/sort_helper.rb index d6ac8bd22..c6bf5383e 100644 --- a/app/helpers/sort_helper.rb +++ b/app/helpers/sort_helper.rb @@ -53,97 +53,6 @@ # module SortHelper - class SortCriteria - - def initialize - @criteria = [] - end - - def available_criteria=(criteria) - unless criteria.is_a?(Hash) - criteria = criteria.inject({}) {|h,k| h[k] = k; h} - end - @available_criteria = criteria - end - - def from_param(param) - @criteria = param.to_s.split(',').collect {|s| s.split(':')[0..1]} - normalize! - end - - def criteria=(arg) - @criteria = arg - normalize! - end - - def to_param - @criteria.collect {|k,o| k + (o ? '' : ':desc')}.join(',') - end - - # Returns an array of SQL fragments used to sort the list - def to_sql - sql = @criteria.collect do |k,o| - if s = @available_criteria[k] - s = [s] unless s.is_a?(Array) - s.collect {|c| append_order(c, o ? "ASC" : "DESC")} - end - end.flatten.compact - sql.blank? ? nil : sql - end - - def to_a - @criteria.dup - end - - def add!(key, asc) - @criteria.delete_if {|k,o| k == key} - @criteria = [[key, asc]] + @criteria - normalize! - end - - def add(*args) - r = self.class.new.from_param(to_param) - r.add!(*args) - r - end - - def first_key - @criteria.first && @criteria.first.first - end - - def first_asc? - @criteria.first && @criteria.first.last - end - - def empty? - @criteria.empty? - end - - private - - def normalize! - @criteria ||= [] - @criteria = @criteria.collect {|s| s = Array(s); [s.first, (s.last == false || s.last == 'desc') ? false : true]} - @criteria = @criteria.select {|k,o| @available_criteria.has_key?(k)} if @available_criteria - @criteria.slice!(3) - self - end - - # Appends ASC/DESC to the sort criterion unless it has a fixed order - def append_order(criterion, order) - if criterion =~ / (asc|desc)$/i - criterion - else - "#{criterion} #{order}" - end - end - - # Appends DESC to the sort criterion unless it has a fixed order - def append_desc(criterion) - append_order(criterion, "DESC") - end - end - def sort_name controller_name + '_' + action_name + '_sort' end @@ -173,10 +82,8 @@ module SortHelper # def sort_update(criteria, sort_name=nil) sort_name ||= self.sort_name - @sort_criteria = SortCriteria.new - @sort_criteria.available_criteria = criteria - @sort_criteria.from_param(params[:sort] || session[sort_name]) - @sort_criteria.criteria = @sort_default if @sort_criteria.empty? + @sort_criteria = Redmine::SortCriteria.new(params[:sort] || session[sort_name] || @sort_default) + @sortable_columns = criteria session[sort_name] = @sort_criteria.to_param end @@ -190,7 +97,7 @@ module SortHelper # Use this to sort the controller's table items collection. # def sort_clause() - @sort_criteria.to_sql + @sort_criteria.sort_clause(@sortable_columns) end def sort_criteria @@ -218,12 +125,7 @@ module SortHelper caption = column.to_s.humanize unless caption sort_options = { :sort => @sort_criteria.add(column.to_s, order).to_param } - url_options = params.merge(sort_options) - - # Add project_id to url_options - url_options = url_options.merge(:project_id => params[:project_id]) if params.has_key?(:project_id) - - link_to_content_update(h(caption), url_options, :class => css) + link_to(caption, {:params => request.query_parameters.merge(sort_options)}, :class => css) end # Returns a table header tag with a sort link for the named column diff --git a/app/helpers/timelog_helper.rb b/app/helpers/timelog_helper.rb index 25bc750f3..d1174fe78 100644 --- a/app/helpers/timelog_helper.rb +++ b/app/helpers/timelog_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -20,20 +20,6 @@ module TimelogHelper include ApplicationHelper - def render_timelog_breadcrumb - links = [] - links << link_to(l(:label_project_all), {:project_id => nil, :issue_id => nil}) - links << link_to(h(@project), {:project_id => @project, :issue_id => nil}) if @project - if @issue - if @issue.visible? - links << link_to_issue(@issue, :subject => false) - else - links << "##{@issue.id}" - end - end - breadcrumb links - end - # Returns a collection of activities for a select field. time_entry # is optional and will be used to check if the selected TimeEntryActivity # is active. diff --git a/app/helpers/trackers_helper.rb b/app/helpers/trackers_helper.rb index 96dbfe2e7..ffb2ca3de 100644 --- a/app/helpers/trackers_helper.rb +++ b/app/helpers/trackers_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index 8b85e9f50..d7812867b 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -30,6 +30,10 @@ module UsersHelper user.valid_notification_options.collect {|o| [l(o.last), o.first]} end + def textarea_font_options + [[l(:label_font_default), '']] + UserPreference::TEXTAREA_FONT_OPTIONS.map {|o| [l("label_font_#{o}"), o]} + end + def change_status_link(user) url = {:controller => 'users', :action => 'update', :id => user, :page => params[:page], :status => params[:status], :tab => nil} diff --git a/app/helpers/versions_helper.rb b/app/helpers/versions_helper.rb index 345784dd5..fe1fb8815 100644 --- a/app/helpers/versions_helper.rb +++ b/app/helpers/versions_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/watchers_helper.rb b/app/helpers/watchers_helper.rb index 7ed7b8914..ad8cd5995 100644 --- a/app/helpers/watchers_helper.rb +++ b/app/helpers/watchers_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -47,7 +47,7 @@ module WatchersHelper def watchers_list(object) remove_allowed = User.current.allowed_to?("delete_#{object.class.name.underscore}_watchers".to_sym, object.project) content = ''.html_safe - lis = object.watcher_users.collect do |user| + lis = object.watcher_users.preload(:email_address).collect do |user| s = ''.html_safe s << avatar(user, :size => "16").to_s s << link_to_user(user, :class => 'user') diff --git a/app/helpers/welcome_helper.rb b/app/helpers/welcome_helper.rb index d1e731d39..cc8ab380b 100644 --- a/app/helpers/welcome_helper.rb +++ b/app/helpers/welcome_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb index 89da54872..2a49f0871 100644 --- a/app/helpers/wiki_helper.rb +++ b/app/helpers/wiki_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/workflows_helper.rb b/app/helpers/workflows_helper.rb index eda40437c..142569e05 100644 --- a/app/helpers/workflows_helper.rb +++ b/app/helpers/workflows_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/attachment.rb b/app/models/attachment.rb index db80db396..64ac2c2d1 100644 --- a/app/models/attachment.rb +++ b/app/models/attachment.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -15,10 +15,11 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -require "digest/md5" +require "digest" require "fileutils" class Attachment < ActiveRecord::Base + include Redmine::SafeAttributes belongs_to :container, :polymorphic => true belongs_to :author, :class_name => "User" @@ -30,7 +31,7 @@ class Attachment < ActiveRecord::Base attr_protected :id acts_as_event :title => :filename, - :url => Proc.new {|o| {:controller => 'attachments', :action => 'download', :id => o.id, :filename => o.filename}} + :url => Proc.new {|o| {:controller => 'attachments', :action => 'show', :id => o.id, :filename => o.filename}} acts_as_activity_provider :type => 'files', :permission => :view_files, @@ -55,6 +56,9 @@ class Attachment < ActiveRecord::Base before_create :files_to_final_location after_rollback :delete_from_disk, :on => :create after_commit :delete_from_disk, :on => :destroy + after_commit :reuse_existing_file_if_possible, :on => :create + + safe_attributes 'filename', 'content_type', 'description' # Returns an unsaved copy of the attachment def copy(attributes=nil) @@ -113,20 +117,20 @@ class Attachment < ActiveRecord::Base unless File.directory?(path) FileUtils.mkdir_p(path) end - md5 = Digest::MD5.new + sha = Digest::SHA256.new File.open(diskfile, "wb") do |f| if @temp_file.respond_to?(:read) buffer = "" while (buffer = @temp_file.read(8192)) f.write(buffer) - md5.update(buffer) + sha.update(buffer) end else f.write(@temp_file) - md5.update(@temp_file) + sha.update(@temp_file) end end - self.digest = md5.hexdigest + self.digest = sha.hexdigest end @temp_file = nil @@ -247,9 +251,13 @@ class Attachment < ActiveRecord::Base Redmine::MimeType.of(filename) == "application/pdf" end + def previewable? + is_text? || is_image? + end + # Returns true if the file is readable def readable? - File.readable?(diskfile) + disk_filename.present? && File.readable?(diskfile) end # Returns the attachment token @@ -349,24 +357,100 @@ class Attachment < ActiveRecord::Base end end - # Returns true if the extension is allowed, otherwise false - def self.valid_extension?(extension) - extension = extension.downcase.sub(/\A\.+/, '') - - denied, allowed = [:attachment_extensions_denied, :attachment_extensions_allowed].map do |setting| - Setting.send(setting).to_s.split(",").map {|s| s.strip.downcase.sub(/\A\.+/, '')}.reject(&:blank?) + # Updates digests to SHA256 for all attachments that have a MD5 digest + # (ie. created before Redmine 3.4) + def self.update_digests_to_sha256 + Attachment.where("length(digest) < 64").find_each do |attachment| + attachment.update_digest_to_sha256! end - if denied.present? && denied.include?(extension) + end + + # Updates attachment digest to SHA256 + def update_digest_to_sha256! + if readable? + sha = Digest::SHA256.new + File.open(diskfile, 'rb') do |f| + while buffer = f.read(8192) + sha.update(buffer) + end + end + update_column :digest, sha.hexdigest + end + end + + # Returns true if the extension is allowed regarding allowed/denied + # extensions defined in application settings, otherwise false + def self.valid_extension?(extension) + denied, allowed = [:attachment_extensions_denied, :attachment_extensions_allowed].map do |setting| + Setting.send(setting) + end + if denied.present? && extension_in?(extension, denied) return false end - unless allowed.blank? || allowed.include?(extension) + if allowed.present? && !extension_in?(extension, allowed) return false end true end + # Returns true if extension belongs to extensions list. + def self.extension_in?(extension, extensions) + extension = extension.downcase.sub(/\A\.+/, '') + + unless extensions.is_a?(Array) + extensions = extensions.to_s.split(",").map(&:strip) + end + extensions = extensions.map {|s| s.downcase.sub(/\A\.+/, '')}.reject(&:blank?) + extensions.include?(extension) + end + + # Returns true if attachment's extension belongs to extensions list. + def extension_in?(extensions) + self.class.extension_in?(File.extname(filename), extensions) + end + + # returns either MD5 or SHA256 depending on the way self.digest was computed + def digest_type + digest.size < 64 ? "MD5" : "SHA256" if digest.present? + end + private + def reuse_existing_file_if_possible + original_diskfile = nil + + reused = with_lock do + if existing = Attachment + .where(digest: self.digest, filesize: self.filesize) + .where('id <> ? and disk_filename <> ?', + self.id, self.disk_filename) + .first + existing.with_lock do + + original_diskfile = self.diskfile + existing_diskfile = existing.diskfile + + if File.readable?(original_diskfile) && + File.readable?(existing_diskfile) && + FileUtils.identical?(original_diskfile, existing_diskfile) + + self.update_columns disk_directory: existing.disk_directory, + disk_filename: existing.disk_filename + end + end + end + end + if reused + File.delete(original_diskfile) + end + rescue ActiveRecord::StatementInvalid, ActiveRecord::RecordNotFound + # Catch and ignore lock errors. It is not critical if deduplication does + # not happen, therefore we do not retry. + # with_lock throws ActiveRecord::RecordNotFound if the record isnt there + # anymore, thats why this is caught and ignored as well. + end + + # Physically deletes the file from the file system def delete_from_disk! if disk_filename.present? && File.exist?(diskfile) @@ -393,7 +477,7 @@ class Attachment < ActiveRecord::Base def self.disk_filename(filename, directory=nil) timestamp = DateTime.now.strftime("%y%m%d%H%M%S") ascii = '' - if filename =~ %r{^[a-zA-Z0-9_\.\-]*$} + if filename =~ %r{^[a-zA-Z0-9_\.\-]*$} && filename.length <= 50 ascii = filename else ascii = Digest::MD5.hexdigest(filename) diff --git a/app/models/auth_source.rb b/app/models/auth_source.rb index 69448d350..4954c962d 100644 --- a/app/models/auth_source.rb +++ b/app/models/auth_source.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -21,6 +21,7 @@ class AuthSourceException < Exception; end class AuthSourceTimeoutException < AuthSourceException; end class AuthSource < ActiveRecord::Base + include Redmine::SafeAttributes include Redmine::SubclassFactory include Redmine::Ciphering @@ -31,6 +32,21 @@ class AuthSource < ActiveRecord::Base validates_length_of :name, :maximum => 60 attr_protected :id + safe_attributes 'name', + 'host', + 'port', + 'account', + 'account_password', + 'base_dn', + 'attr_login', + 'attr_firstname', + 'attr_lastname', + 'attr_mail', + 'onthefly_register', + 'tls', + 'filter', + 'timeout' + def authenticate(login, password) end diff --git a/app/models/auth_source_ldap.rb b/app/models/auth_source_ldap.rb index d2aa25212..c3565aa8f 100644 --- a/app/models/auth_source_ldap.rb +++ b/app/models/auth_source_ldap.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/board.rb b/app/models/board.rb index 39ad4a59f..21461e32f 100644 --- a/app/models/board.rb +++ b/app/models/board.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/change.rb b/app/models/change.rb index 9e32bce00..e0d25f3a9 100644 --- a/app/models/change.rb +++ b/app/models/change.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/changeset.rb b/app/models/changeset.rb index edfe85756..4256f0589 100644 --- a/app/models/changeset.rb +++ b/app/models/changeset.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -247,6 +247,8 @@ class Changeset < ActiveRecord::Base unless issue.save logger.warn("Issue ##{issue.id} could not be saved by changeset #{id}: #{issue.errors.full_messages}") if logger end + else + issue.clear_journal end issue end diff --git a/app/models/comment.rb b/app/models/comment.rb index 4912b57d0..48b47d970 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/custom_field.rb b/app/models/custom_field.rb index fe39b628f..ca061a25f 100644 --- a/app/models/custom_field.rb +++ b/app/models/custom_field.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,6 +16,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class CustomField < ActiveRecord::Base + include Redmine::SafeAttributes include Redmine::SubclassFactory has_many :enumerations, @@ -62,11 +63,35 @@ class CustomField < ActiveRecord::Base where(:visible => true) end } - def visible_by?(project, user=User.current) visible? || user.admin? end + safe_attributes 'name', + 'field_format', + 'possible_values', + 'regexp', + 'min_length', + 'max_length', + 'is_required', + 'is_for_all', + 'is_filter', + 'position', + 'searchable', + 'default_value', + 'editable', + 'visible', + 'multiple', + 'description', + 'role_ids', + 'url_pattern', + 'text_formatting', + 'edit_tag_style', + 'user_role', + 'version_status', + 'extensions_allowed', + 'full_width_layout' + def format @format ||= Redmine::FieldFormat.find(field_format) end @@ -141,6 +166,10 @@ class CustomField < ActiveRecord::Base end end + def set_custom_field_value(custom_field_value, value) + format.set_custom_field_value(self, custom_field_value, value) + end + def cast_value(value) format.cast_value(self, value) end @@ -158,6 +187,10 @@ class CustomField < ActiveRecord::Base format.totalable_supported end + def full_width_layout? + full_width_layout == '1' + end + # Returns a ORDER BY clause that can used to sort customized # objects by their value of the custom field. # Returns nil if the custom field can not be used for sorting. @@ -221,7 +254,7 @@ class CustomField < ActiveRecord::Base # to move in project_custom_field def self.for_all - where(:is_for_all => true).order('position').to_a + where(:is_for_all => true).order(:position).to_a end def type_name @@ -232,20 +265,23 @@ class CustomField < ActiveRecord::Base # or an empty array if value is a valid value for the custom field def validate_custom_value(custom_value) value = custom_value.value - errs = [] - if value.is_a?(Array) - if !multiple? - errs << ::I18n.t('activerecord.errors.messages.invalid') - end - if is_required? && value.detect(&:present?).nil? - errs << ::I18n.t('activerecord.errors.messages.blank') - end - else - if is_required? && value.blank? - errs << ::I18n.t('activerecord.errors.messages.blank') + errs = format.validate_custom_value(custom_value) + + unless errs.any? + if value.is_a?(Array) + if !multiple? + errs << ::I18n.t('activerecord.errors.messages.invalid') + end + if is_required? && value.detect(&:present?).nil? + errs << ::I18n.t('activerecord.errors.messages.blank') + end + else + if is_required? && value.blank? + errs << ::I18n.t('activerecord.errors.messages.blank') + end end end - errs += format.validate_custom_value(custom_value) + errs end @@ -259,6 +295,10 @@ class CustomField < ActiveRecord::Base validate_field_value(value).empty? end + def after_save_custom_value(custom_value) + format.after_save_custom_value(self, custom_value) + end + def format_in?(*args) args.include?(field_format) end diff --git a/app/models/custom_field_enumeration.rb b/app/models/custom_field_enumeration.rb index b7b3cd60b..125cd41b0 100644 --- a/app/models/custom_field_enumeration.rb +++ b/app/models/custom_field_enumeration.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,7 +17,6 @@ class CustomFieldEnumeration < ActiveRecord::Base belongs_to :custom_field - attr_accessible :name, :active, :position validates_presence_of :name, :position, :custom_field_id validates_length_of :name, :maximum => 60 @@ -51,12 +50,15 @@ class CustomFieldEnumeration < ActiveRecord::Base end def self.update_each(custom_field, attributes) - return unless attributes.is_a?(Hash) transaction do attributes.each do |enumeration_id, enumeration_attributes| enumeration = custom_field.enumerations.find_by_id(enumeration_id) if enumeration - enumeration.attributes = enumeration_attributes + if block_given? + yield enumeration, enumeration_attributes + else + enumeration.attributes = enumeration_attributes + end unless enumeration.save raise ActiveRecord::Rollback end diff --git a/app/models/custom_field_value.rb b/app/models/custom_field_value.rb index a816f0d11..b5481e164 100644 --- a/app/models/custom_field_value.rb +++ b/app/models/custom_field_value.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -48,6 +48,18 @@ class CustomFieldValue value.to_s end + def value=(v) + @value = custom_field.set_custom_field_value(self, v) + end + + def value_present? + if value.is_a?(Array) + value.any?(&:present?) + else + value.present? + end + end + def validate_value custom_field.validate_custom_value(self).each do |message| customized.errors.add(:base, custom_field.name + ' ' + message) diff --git a/app/models/custom_value.rb b/app/models/custom_value.rb index 8026d5f65..33da1bd8e 100644 --- a/app/models/custom_value.rb +++ b/app/models/custom_value.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -20,6 +20,8 @@ class CustomValue < ActiveRecord::Base belongs_to :customized, :polymorphic => true attr_protected :id + after_save :custom_field_after_save_custom_value + def initialize(attributes=nil, *args) super if new_record? && custom_field && !attributes.key?(:value) @@ -36,8 +38,18 @@ class CustomValue < ActiveRecord::Base custom_field.editable? end - def visible? - custom_field.visible? + def visible?(user=User.current) + if custom_field.visible? + true + elsif customized.respond_to?(:project) + custom_field.visible_by?(customized.project, user) + else + false + end + end + + def attachments_visible?(user) + visible?(user) && customized && customized.visible?(user) end def required? @@ -47,4 +59,10 @@ class CustomValue < ActiveRecord::Base def to_s value.to_s end + + private + + def custom_field_after_save_custom_value + custom_field.after_save_custom_value(self) + end end diff --git a/app/models/document.rb b/app/models/document.rb index c53656766..d347e583c 100644 --- a/app/models/document.rb +++ b/app/models/document.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/document_category.rb b/app/models/document_category.rb index c5fde10bc..218c6cde6 100644 --- a/app/models/document_category.rb +++ b/app/models/document_category.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/document_category_custom_field.rb b/app/models/document_category_custom_field.rb index e4563682f..9ff5ee768 100644 --- a/app/models/document_category_custom_field.rb +++ b/app/models/document_category_custom_field.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/document_custom_field.rb b/app/models/document_custom_field.rb index 31b230dbd..7b689e80c 100644 --- a/app/models/document_custom_field.rb +++ b/app/models/document_custom_field.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/email_address.rb b/app/models/email_address.rb index ae16b9434..295b9bcaa 100644 --- a/app/models/email_address.rb +++ b/app/models/email_address.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,6 +16,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class EmailAddress < ActiveRecord::Base + include Redmine::SafeAttributes + belongs_to :user attr_protected :id @@ -29,6 +31,8 @@ class EmailAddress < ActiveRecord::Base validates_uniqueness_of :address, :case_sensitive => false, :if => Proc.new {|email| email.address_changed? && email.address.present?} + safe_attributes 'address' + def address=(arg) write_attribute(:address, arg.to_s.strip) end diff --git a/app/models/enabled_module.rb b/app/models/enabled_module.rb index 480061ff9..2548ba25d 100644 --- a/app/models/enabled_module.rb +++ b/app/models/enabled_module.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/enumeration.rb b/app/models/enumeration.rb index fc8486174..eef691c4c 100644 --- a/app/models/enumeration.rb +++ b/app/models/enumeration.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -136,7 +136,7 @@ class Enumeration < ActiveRecord::Base end # Overrides Redmine::Acts::Positioned#set_default_position so that enumeration overrides - # get the same position as the overriden enumeration + # get the same position as the overridden enumeration def set_default_position if position.nil? && parent self.position = parent.position @@ -145,7 +145,7 @@ class Enumeration < ActiveRecord::Base end # Overrides Redmine::Acts::Positioned#update_position so that overrides get the same - # position as the overriden enumeration + # position as the overridden enumeration def update_position super if position_changed? @@ -159,7 +159,7 @@ class Enumeration < ActiveRecord::Base end # Overrides Redmine::Acts::Positioned#remove_position so that enumeration overrides - # get the same position as the overriden enumeration + # get the same position as the overridden enumeration def remove_position if parent_id.blank? super diff --git a/app/models/group.rb b/app/models/group.rb index 1362472b5..d94ef753c 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/group_anonymous.rb b/app/models/group_anonymous.rb index b872c27c1..a3e5f381c 100644 --- a/app/models/group_anonymous.rb +++ b/app/models/group_anonymous.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/group_builtin.rb b/app/models/group_builtin.rb index 29e1d8e2a..911e274d7 100644 --- a/app/models/group_builtin.rb +++ b/app/models/group_builtin.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/group_custom_field.rb b/app/models/group_custom_field.rb index fe70d76c6..76755ecbe 100644 --- a/app/models/group_custom_field.rb +++ b/app/models/group_custom_field.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/group_non_member.rb b/app/models/group_non_member.rb index 1d4d6f7d4..d94666ac6 100644 --- a/app/models/group_non_member.rb +++ b/app/models/group_non_member.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/import.rb b/app/models/import.rb index 22b90b6ac..d2c53baac 100644 --- a/app/models/import.rb +++ b/app/models/import.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -138,6 +138,24 @@ class Import < ActiveRecord::Base settings['mapping'] || {} end + # Adds a callback that will be called after the item at given position is imported + def add_callback(position, name, *args) + settings['callbacks'] ||= {} + settings['callbacks'][position.to_i] ||= [] + settings['callbacks'][position.to_i] << [name, args] + save! + end + + # Executes the callbacks for the given object + def do_callbacks(position, object) + if callbacks = (settings['callbacks'] || {}).delete(position) + callbacks.each do |name, args| + send "#{name}_callback", object, *args + end + save! + end + end + # Imports items and returns the position of the last processed item def run(options={}) max_items = options[:max_items] @@ -157,7 +175,7 @@ class Import < ActiveRecord::Base item = items.build item.position = position - if object = build_object(row) + if object = build_object(row, item) if object.save item.obj_id = object.id else @@ -167,6 +185,8 @@ class Import < ActiveRecord::Base item.save! imported += 1 + + do_callbacks(item.position, object) end current = position end diff --git a/app/models/import_item.rb b/app/models/import_item.rb index c02c0bc5f..74d5c3797 100644 --- a/app/models/import_item.rb +++ b/app/models/import_item.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/issue.rb b/app/models/issue.rb index 3eb06b645..eeae2c70b 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -32,11 +32,6 @@ class Issue < ActiveRecord::Base belongs_to :category, :class_name => 'IssueCategory' has_many :journals, :as => :journalized, :dependent => :destroy, :inverse_of => :journalized - has_many :visible_journals, - lambda {where(["(#{Journal.table_name}.private_notes = ? OR (#{Project.allowed_to_condition(User.current, :view_private_notes)}))", false])}, - :class_name => 'Journal', - :as => :journalized - has_many :time_entries, :dependent => :destroy has_and_belongs_to_many :changesets, lambda {order("#{Changeset.table_name}.committed_on ASC, #{Changeset.table_name}.id ASC")} @@ -52,13 +47,14 @@ class Issue < ActiveRecord::Base acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id} (#{o.status}): #{o.subject}"}, :url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.id}}, - :type => Proc.new {|o| 'issue' + (o.closed? ? ' closed' : '') } + :type => Proc.new {|o| 'issue' + (o.closed? ? '-closed' : '') } acts_as_activity_provider :scope => preload(:project, :author, :tracker, :status), :author_key => :author_id DONE_RATIO_OPTIONS = %w(issue_field issue_status) + attr_accessor :deleted_attachment_ids attr_reader :current_journal delegate :notes, :notes=, :private_notes, :private_notes=, :to => :current_journal, :allow_nil => true @@ -83,17 +79,17 @@ class Issue < ActiveRecord::Base scope :open, lambda {|*args| is_closed = args.size > 0 ? !args.first : false joins(:status). - where("#{IssueStatus.table_name}.is_closed = ?", is_closed) + where(:issue_statuses => {:is_closed => is_closed}) } - scope :recently_updated, lambda { order("#{Issue.table_name}.updated_on DESC") } + scope :recently_updated, lambda { order(:updated_on => :desc) } scope :on_active_project, lambda { joins(:project). - where("#{Project.table_name}.status = ?", Project::STATUS_ACTIVE) + where(:projects => {:status => Project::STATUS_ACTIVE}) } scope :fixed_version, lambda {|versions| ids = [versions].flatten.compact.map {|v| v.is_a?(Version) ? v.id : v} - ids.any? ? where(:fixed_version_id => ids) : where('1=0') + ids.any? ? where(:fixed_version_id => ids) : none } scope :assigned_to, lambda {|arg| arg = Array(arg).uniq @@ -102,6 +98,12 @@ class Issue < ActiveRecord::Base ids.compact! ids.any? ? where(:assigned_to_id => ids) : none } + scope :like, lambda {|q| + q = q.to_s + if q.present? + where("LOWER(#{table_name}.subject) LIKE LOWER(?)", "%#{q}%") + end + } before_validation :default_assign, on: :create before_validation :clear_disabled_fields @@ -109,13 +111,11 @@ class Issue < ActiveRecord::Base :force_updated_on_change, :update_closed_on, :set_assigned_to_was after_save {|issue| issue.send :after_project_change if !issue.id_changed? && issue.project_id_changed?} after_save :reschedule_following_issues, :update_nested_set_attributes, - :update_parent_attributes, :create_journal + :update_parent_attributes, :delete_selected_attachments, :create_journal # Should be after_create but would be called before previous after_save callbacks after_save :after_create_from_copy after_destroy :update_parent_attributes after_create :send_notification - # Keep it at the end of after_save callbacks - after_save :clear_assigned_to_was # Returns a SQL conditions string used to find all issues visible by the specified user def self.visible_condition(user, options={}) @@ -243,6 +243,8 @@ class Issue < ActiveRecord::Base @spent_hours = nil @total_spent_hours = nil @total_estimated_hours = nil + @last_updated_by = nil + @last_notes = nil base_reload(*args) end @@ -261,15 +263,21 @@ class Issue < ActiveRecord::Base # Copies attributes from another issue, arg can be an id or an Issue def copy_from(arg, options={}) issue = arg.is_a?(Issue) ? arg : Issue.visible.find(arg) - self.attributes = issue.attributes.dup.except("id", "root_id", "parent_id", "lft", "rgt", "created_on", "updated_on", "closed_on") + self.attributes = issue.attributes.dup.except("id", "root_id", "parent_id", "lft", "rgt", "created_on", "updated_on", "status_id", "closed_on") self.custom_field_values = issue.custom_field_values.inject({}) {|h,v| h[v.custom_field_id] = v.value; h} - self.status = issue.status + if options[:keep_status] + self.status = issue.status + end self.author = User.current unless options[:attachments] == false self.attachments = issue.attachments.map do |attachement| attachement.copy(:container => self) end end + unless options[:watchers] == false + self.watcher_user_ids = + issue.watcher_users.select{|u| u.status == User::STATUS_ACTIVE}.map(&:id) + end @copied_from = issue @copy_options = options self @@ -366,6 +374,9 @@ class Issue < ActiveRecord::Base def project=(project, keep_tracker=false) project_was = self.project association(:project).writer(project) + if project != project_was + @safe_attribute_names = nil + end if project_was && project && project_was != project @assignable_versions = nil @@ -376,6 +387,11 @@ class Issue < ActiveRecord::Base if category self.category = project.issue_categories.find_by_name(category.name) end + # Clear the assignee if not available in the new project for new issues (eg. copy) + # For existing issue, the previous assignee is still valid, so we keep it + if new_record? && assigned_to && !assignable_users.include?(assigned_to) + self.assigned_to_id = nil + end # Keep the fixed_version if it's still valid in the new_project if fixed_version && fixed_version.project != project && !project.shared_versions.include?(fixed_version) self.fixed_version = nil @@ -403,8 +419,12 @@ class Issue < ActiveRecord::Base write_attribute(:description, arg) end + def deleted_attachment_ids + Array(@deleted_attachment_ids).map(&:to_i) + end + # Overrides assign_attributes so that project and tracker get assigned first - def assign_attributes_with_project_and_tracker_first(new_attributes, *args) + def assign_attributes(new_attributes, *args) return if new_attributes.nil? attrs = new_attributes.dup attrs.stringify_keys! @@ -414,10 +434,8 @@ class Issue < ActiveRecord::Base send "#{attr}=", attrs.delete(attr) end end - send :assign_attributes_without_project_and_tracker_first, attrs, *args + super attrs, *args end - # Do not redefine alias chain on reload (see #4838) - alias_method_chain(:assign_attributes, :project_and_tracker_first) unless method_defined?(:assign_attributes_without_project_and_tracker_first) def attributes=(new_attributes) assign_attributes new_attributes @@ -465,6 +483,9 @@ class Issue < ActiveRecord::Base :if => lambda {|issue, user| (issue.new_record? || issue.attributes_editable?(user)) && user.allowed_to?(:manage_subtasks, issue.project)} + safe_attributes 'deleted_attachment_ids', + :if => lambda {|issue, user| issue.attachments_deletable?(user)} + def safe_attribute_names(user=nil) names = super names -= disabled_core_fields @@ -517,10 +538,17 @@ class Issue < ActiveRecord::Base self.tracker_id = t end end - if project + if project && tracker.nil? # Set a default tracker to accept custom field values # even if tracker is not specified - self.tracker ||= allowed_target_trackers(user).first + allowed_trackers = allowed_target_trackers(user) + + if attrs['parent_issue_id'].present? + # If parent_issue_id is present, the first tracker for which this field + # is not disabled is chosen as default + self.tracker = allowed_trackers.detect {|t| t.core_fields.include?('parent_issue_id')} + end + self.tracker ||= allowed_trackers.first end statuses_allowed = new_statuses_allowed_to(user) @@ -533,14 +561,7 @@ class Issue < ActiveRecord::Base self.status = statuses_allowed.first || default_status end if (u = attrs.delete('assigned_to_id')) && safe_attribute?('assigned_to_id') - if u.blank? - self.assigned_to_id = nil - else - u = u.to_i - if assignable_users.any?{|assignable_user| assignable_user.id == u} - self.assigned_to_id = u - end - end + self.assigned_to_id = u end @@ -703,6 +724,12 @@ class Issue < ActiveRecord::Base end end + if assigned_to_id_changed? && assigned_to_id.present? + unless assignable_users.include?(assigned_to) + errors.add :assigned_to_id, :invalid + end + end + # Checks parent issue assignment if @invalid_parent_issue_id.present? errors.add :parent_issue_id, :invalid @@ -714,6 +741,9 @@ class Issue < ActiveRecord::Base @parent_issue.self_and_ancestors.any? {|a| a.relations_from.any? {|r| r.relation_type == IssueRelation::TYPE_PRECEDES && r.issue_to.would_reschedule?(self)}} ) errors.add :parent_issue_id, :invalid + elsif !closed? && @parent_issue.closed? + # cannot attach an open issue to a closed parent + errors.add :base, :open_issue_with_closed_parent elsif !new_record? # moving an existing issue if move_possible?(@parent_issue) @@ -782,6 +812,11 @@ class Issue < ActiveRecord::Base @current_journal end + # Clears the current journal + def clear_journal + @current_journal = nil + end + # Returns the names of attributes that are journalized when updating the issue def journalized_attribute_names names = Issue.column_names - %w(id root_id lft rgt lock_version created_on updated_on closed_on) @@ -809,6 +844,26 @@ class Issue < ActiveRecord::Base scope end + # Returns the journals that are visible to user with their index + # Used to display the issue history + def visible_journals_with_index(user=User.current) + result = journals. + preload(:details). + preload(:user => :email_address). + reorder(:created_on, :id).to_a + + result.each_with_index {|j,i| j.indice = i+1} + + unless user.allowed_to?(:view_private_notes, project) + result.select! do |journal| + !journal.private_notes? || journal.user == user + end + end + Journal.preload_journals_details_custom_fields(result) + result.select! {|journal| journal.notes? || journal.visible_details.any?} + result + end + # Returns the initial status of the issue # Returns nil for a new issue def status_was @@ -871,7 +926,9 @@ class Issue < ActiveRecord::Base def assignable_users users = project.assignable_users(tracker).to_a users << author if author && author.active? - users << assigned_to if assigned_to + if assigned_to_id_was.present? && assignee = Principal.find_by_id(assigned_to_id_was) + users << assignee + end users.uniq.sort end @@ -907,44 +964,46 @@ class Issue < ActiveRecord::Base # Returns an array of statuses that user is able to apply def new_statuses_allowed_to(user=User.current, include_default=false) - if new_record? && @copied_from - [default_status, @copied_from.status].compact.uniq.sort - else - initial_status = nil - if new_record? - # nop - elsif tracker_id_changed? - if Tracker.where(:id => tracker_id_was, :default_status_id => status_id_was).any? - initial_status = default_status - elsif tracker.issue_status_ids.include?(status_id_was) - initial_status = IssueStatus.find_by_id(status_id_was) - else - initial_status = default_status - end + initial_status = nil + if new_record? + # nop + elsif tracker_id_changed? + if Tracker.where(:id => tracker_id_was, :default_status_id => status_id_was).any? + initial_status = default_status + elsif tracker.issue_status_ids.include?(status_id_was) + initial_status = IssueStatus.find_by_id(status_id_was) else - initial_status = status_was + initial_status = default_status end - - initial_assigned_to_id = assigned_to_id_changed? ? assigned_to_id_was : assigned_to_id - assignee_transitions_allowed = initial_assigned_to_id.present? && - (user.id == initial_assigned_to_id || user.group_ids.include?(initial_assigned_to_id)) - - statuses = [] - statuses += IssueStatus.new_statuses_allowed( - initial_status, - user.admin ? Role.all.to_a : user.roles_for_project(project), - tracker, - author == user, - assignee_transitions_allowed - ) - statuses << initial_status unless statuses.empty? - statuses << default_status if include_default || (new_record? && statuses.empty?) - statuses = statuses.compact.uniq.sort - if blocked? - statuses.reject!(&:is_closed?) - end - statuses + else + initial_status = status_was end + + initial_assigned_to_id = assigned_to_id_changed? ? assigned_to_id_was : assigned_to_id + assignee_transitions_allowed = initial_assigned_to_id.present? && + (user.id == initial_assigned_to_id || user.group_ids.include?(initial_assigned_to_id)) + + statuses = [] + statuses += IssueStatus.new_statuses_allowed( + initial_status, + user.admin ? Role.all.to_a : user.roles_for_project(project), + tracker, + author == user, + assignee_transitions_allowed + ) + statuses << initial_status unless statuses.empty? + statuses << default_status if include_default || (new_record? && statuses.empty?) + + statuses = statuses.compact.uniq.sort + if blocked? || descendants.open.any? + # cannot close a blocked issue or a parent with open subtasks + statuses.reject!(&:is_closed?) + end + if ancestors.open(false).any? + # cannot reopen a subtask of a closed parent + statuses.select!(&:is_closed?) + end + statuses end # Returns the previous assignee (user or group) if changed @@ -1036,6 +1095,22 @@ class Issue < ActiveRecord::Base @relations ||= IssueRelation::Relations.new(self, (relations_from + relations_to).sort) end + def last_updated_by + if @last_updated_by + @last_updated_by.presence + else + journals.reorder(:id => :desc).first.try(:user) + end + end + + def last_notes + if @last_notes + @last_notes + else + journals.where.not(notes: '').reorder(:id => :desc).first.try(:notes) + end + end + # Preloads relations for a collection of issues def self.load_relations(issues) if issues.any? @@ -1099,6 +1174,45 @@ class Issue < ActiveRecord::Base where(:ancestors => {:id => issues.map(&:id)}) end + # Preloads users who updated last a collection of issues + def self.load_visible_last_updated_by(issues, user=User.current) + if issues.any? + issue_ids = issues.map(&:id) + journal_ids = Journal.joins(issue: :project). + where(:journalized_type => 'Issue', :journalized_id => issue_ids). + where(Journal.visible_notes_condition(user, :skip_pre_condition => true)). + group(:journalized_id). + maximum(:id). + values + journals = Journal.where(:id => journal_ids).preload(:user).to_a + + issues.each do |issue| + journal = journals.detect {|j| j.journalized_id == issue.id} + issue.instance_variable_set("@last_updated_by", journal.try(:user) || '') + end + end + end + + # Preloads visible last notes for a collection of issues + def self.load_visible_last_notes(issues, user=User.current) + if issues.any? + issue_ids = issues.map(&:id) + journal_ids = Journal.joins(issue: :project). + where(:journalized_type => 'Issue', :journalized_id => issue_ids). + where(Journal.visible_notes_condition(user, :skip_pre_condition => true)). + where.not(notes: ''). + group(:journalized_id). + maximum(:id). + values + journals = Journal.where(:id => journal_ids).to_a + + issues.each do |issue| + journal = journals.detect {|j| j.journalized_id == issue.id} + issue.instance_variable_set("@last_notes", journal.try(:notes) || '') + end + end + end + # Finds an issue relation given its id. def find_relation(relation_id) IssueRelation.where("issue_to_id = ? OR issue_from_id = ?", id, id).find(relation_id) @@ -1165,7 +1279,8 @@ class Issue < ActiveRecord::Base def soonest_start(reload=false) if @soonest_start.nil? || reload - dates = relations_to(reload).collect{|relation| relation.successor_soonest_start} + relations_to.reload if reload + dates = relations_to.collect{|relation| relation.successor_soonest_start} p = @parent_issue || parent if p && Setting.parent_issue_dates == 'derived' dates << p.soonest_start @@ -1478,6 +1593,7 @@ class Issue < ActiveRecord::Base # Change project and keep project child.send :project=, project, true unless child.save + errors.add :base, l(:error_move_of_child_not_possible, :child => "##{child.id}", :errors => child.errors.full_messages.join(", ")) raise ActiveRecord::Rollback end end @@ -1519,6 +1635,8 @@ class Issue < ActiveRecord::Base copy.author = author copy.project = project 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 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 @@ -1578,18 +1696,23 @@ class Issue < ActiveRecord::Base end if p.done_ratio_derived? - # done ratio = weighted average ratio of leaves + # done ratio = average ratio of children weighted with their total estimated hours unless Issue.use_status_for_done_ratio? && p.status && p.status.default_done_ratio - child_count = p.children.count - if child_count > 0 - average = p.children.where("estimated_hours > 0").average(:estimated_hours).to_f - if average == 0 - average = 1 + children = p.children.to_a + if children.any? + child_with_total_estimated_hours = children.select {|c| c.total_estimated_hours.to_f > 0.0} + if child_with_total_estimated_hours.any? + average = child_with_total_estimated_hours.map(&:total_estimated_hours).sum.to_f / child_with_total_estimated_hours.count + else + average = 1.0 end - done = p.children.joins(:status). - sum("COALESCE(CASE WHEN estimated_hours > 0 THEN estimated_hours ELSE NULL END, #{average}) " + - "* (CASE WHEN is_closed = #{self.class.connection.quoted_true} THEN 100 ELSE COALESCE(done_ratio, 0) END)").to_f - progress = done / (average * child_count) + done = children.map {|c| + estimated = c.total_estimated_hours.to_f + estimated = average unless estimated > 0.0 + ratio = c.closed? ? 100 : (c.done_ratio || 0) + estimated * ratio + }.sum + progress = done / (average * children.count) p.done_ratio = progress.round end end @@ -1619,6 +1742,13 @@ class Issue < ActiveRecord::Base end end + def delete_selected_attachments + if deleted_attachment_ids.present? + objects = attachments.where(:id => deleted_attachment_ids.map(&:to_i)) + attachments.delete(objects) + end + end + # Callback on file attachment def attachment_added(attachment) if current_journal && !attachment.new_record? @@ -1650,10 +1780,14 @@ class Issue < ActiveRecord::Base end end - # Default assignment based on category + # Default assignment based on project or category def default_assign - if assigned_to.nil? && category && category.assigned_to - self.assigned_to = category.assigned_to + if assigned_to.nil? + if category && category.assigned_to + self.assigned_to = category.assigned_to + elsif project && project.default_assigned_to + self.assigned_to = project.default_assigned_to + end end end diff --git a/app/models/issue_category.rb b/app/models/issue_category.rb index 6476cfbce..da96a85eb 100644 --- a/app/models/issue_category.rb +++ b/app/models/issue_category.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/issue_custom_field.rb b/app/models/issue_custom_field.rb index 0c679896d..fb7accd34 100644 --- a/app/models/issue_custom_field.rb +++ b/app/models/issue_custom_field.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -20,6 +20,9 @@ class IssueCustomField < CustomField has_and_belongs_to_many :trackers, :join_table => "#{table_name_prefix}custom_fields_trackers#{table_name_suffix}", :foreign_key => "custom_field_id" has_many :issues, :through => :issue_custom_values + safe_attributes 'project_ids', + 'tracker_ids' + def type_name :label_issue_plural end diff --git a/app/models/issue_import.rb b/app/models/issue_import.rb index 4ecd4b517..9fc4f5550 100644 --- a/app/models/issue_import.rb +++ b/app/models/issue_import.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -74,7 +74,7 @@ class IssueImport < Import private - def build_object(row) + def build_object(row, item) issue = Issue.new issue.author = user issue.notify = false @@ -122,7 +122,10 @@ class IssueImport < Import end end 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 elsif create_versions? version = issue.project.versions.build @@ -139,11 +142,15 @@ class IssueImport < Import end if parent_issue_id = row_value(row, 'parent_issue_id') if parent_issue_id =~ /\A(#)?(\d+)\z/ - parent_issue_id = $2 + parent_issue_id = $2.to_i if $1 attributes['parent_issue_id'] = parent_issue_id - elsif issue_id = items.where(:position => parent_issue_id).first.try(:obj_id) - attributes['parent_issue_id'] = issue_id + else + if parent_issue_id > item.position + add_callback(parent_issue_id, 'set_as_parent', item.position) + elsif issue_id = items.where(:position => parent_issue_id).first.try(:obj_id) + attributes['parent_issue_id'] = issue_id + end end else attributes['parent_issue_id'] = parent_issue_id @@ -183,4 +190,17 @@ class IssueImport < Import issue end + + # Callback that sets issue as the parent of a previously imported issue + def set_as_parent_callback(issue, child_position) + child_id = items.where(:position => child_position).first.try(:obj_id) + return unless child_id + + child = Issue.find_by_id(child_id) + return unless child + + child.parent_issue_id = issue.id + child.save! + issue.reload + end end diff --git a/app/models/issue_priority.rb b/app/models/issue_priority.rb index 72b771805..0858cbf0f 100644 --- a/app/models/issue_priority.rb +++ b/app/models/issue_priority.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/issue_priority_custom_field.rb b/app/models/issue_priority_custom_field.rb index ef05b4f14..ffcf2a972 100644 --- a/app/models/issue_priority_custom_field.rb +++ b/app/models/issue_priority_custom_field.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/issue_query.rb b/app/models/issue_query.rb index 7539f427a..d4982cf13 100644 --- a/app/models/issue_query.rb +++ b/app/models/issue_query.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -18,6 +18,7 @@ class IssueQuery < Query self.queried_class = Issue + self.view_permission = :view_issues self.available_columns = [ QueryColumn.new(:id, :sortable => "#{Issue.table_name}.id", :default_order => 'desc', :caption => '#', :frozen => true), @@ -42,66 +43,18 @@ class IssueQuery < Query 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(:closed_on, :sortable => "#{Issue.table_name}.closed_on", :default_order => 'desc'), + QueryColumn.new(:last_updated_by, :sortable => lambda {User.fields_for_order_statement("last_journal_user")}), QueryColumn.new(:relations, :caption => :label_related_issues), - QueryColumn.new(:description, :inline => false) + QueryColumn.new(:attachments, :caption => :label_attachment_plural), + QueryColumn.new(:description, :inline => false), + QueryColumn.new(:last_notes, :caption => :label_last_notes, :inline => false) ] - scope :visible, lambda {|*args| - user = args.shift || User.current - base = Project.allowed_to_condition(user, :view_issues, *args) - scope = joins("LEFT OUTER JOIN #{Project.table_name} ON #{table_name}.project_id = #{Project.table_name}.id"). - where("#{table_name}.project_id IS NULL OR (#{base})") - - if user.admin? - scope.where("#{table_name}.visibility <> ? OR #{table_name}.user_id = ?", VISIBILITY_PRIVATE, user.id) - elsif user.memberships.any? - scope.where("#{table_name}.visibility = ?" + - " OR (#{table_name}.visibility = ? AND #{table_name}.id IN (" + - "SELECT DISTINCT q.id FROM #{table_name} q" + - " 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 #{Member.table_name} m ON m.id = mr.member_id AND m.user_id = ?" + - " WHERE q.project_id IS NULL OR q.project_id = m.project_id))" + - " OR #{table_name}.user_id = ?", - VISIBILITY_PUBLIC, VISIBILITY_ROLES, user.id, user.id) - elsif user.logged? - scope.where("#{table_name}.visibility = ? OR #{table_name}.user_id = ?", VISIBILITY_PUBLIC, user.id) - else - scope.where("#{table_name}.visibility = ?", VISIBILITY_PUBLIC) - end - } - def initialize(attributes=nil, *args) super attributes self.filters ||= { 'status_id' => {:operator => "o", :values => [""]} } end - # Returns true if the query is visible to +user+ or the current user. - def visible?(user=User.current) - return true if user.admin? - return false unless project.nil? || user.allowed_to?(:view_issues, project) - case visibility - when VISIBILITY_PUBLIC - true - when VISIBILITY_ROLES - if project - (user.roles_for_project(project) & roles).any? - else - Member.where(:user_id => user.id).joins(:roles).where(:member_roles => {:role_id => roles.map(&:id)}).any? - end - else - user == self.user - end - end - - def is_private? - visibility == VISIBILITY_PRIVATE - end - - def is_public? - !is_private? - end - def draw_relations r = options[:draw_relations] r.nil? || r == '1' @@ -128,84 +81,50 @@ class IssueQuery < Query end def initialize_available_filters - principals = [] - subprojects = [] - versions = [] - categories = [] - issue_custom_fields = [] - - if project - principals += project.principals.visible - unless project.leaf? - subprojects = project.descendants.visible.to_a - principals += Principal.member_of(subprojects).visible - end - versions = project.shared_versions.to_a - categories = project.issue_categories.to_a - issue_custom_fields = project.all_issue_custom_fields - else - if all_projects.any? - principals += Principal.member_of(all_projects).visible - end - versions = Version.visible.where(:sharing => 'system').to_a - issue_custom_fields = IssueCustomField.where(:is_for_all => true) - end - principals.uniq! - principals.sort! - principals.reject! {|p| p.is_a?(GroupBuiltin)} - users = principals.select {|p| p.is_a?(User)} - add_available_filter "status_id", - :type => :list_status, :values => IssueStatus.sorted.collect{|s| [s.name, s.id.to_s] } + :type => :list_status, :values => lambda { issue_statuses_values } - if project.nil? - project_values = [] - if User.current.logged? && User.current.memberships.any? - project_values << ["<< #{l(:label_my_projects).downcase} >>", "mine"] - end - project_values += all_projects_values - add_available_filter("project_id", - :type => :list, :values => project_values - ) unless project_values.empty? - end + add_available_filter("project_id", + :type => :list, :values => lambda { project_values } + ) if project.nil? add_available_filter "tracker_id", :type => :list, :values => trackers.collect{|s| [s.name, s.id.to_s] } + add_available_filter "priority_id", :type => :list, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s] } - author_values = [] - author_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged? - author_values += users.collect{|s| [s.name, s.id.to_s] } add_available_filter("author_id", - :type => :list, :values => author_values - ) unless author_values.empty? + :type => :list, :values => lambda { author_values } + ) - assigned_to_values = [] - assigned_to_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged? - assigned_to_values += (Setting.issue_group_assignment? ? - principals : users).collect{|s| [s.name, s.id.to_s] } add_available_filter("assigned_to_id", - :type => :list_optional, :values => assigned_to_values - ) unless assigned_to_values.empty? + :type => :list_optional, :values => lambda { assigned_to_values } + ) - group_values = Group.givable.visible.collect {|g| [g.name, g.id.to_s] } add_available_filter("member_of_group", - :type => :list_optional, :values => group_values - ) unless group_values.empty? + :type => :list_optional, :values => lambda { Group.givable.visible.collect {|g| [g.name, g.id.to_s] } } + ) - role_values = Role.givable.collect {|r| [r.name, r.id.to_s] } add_available_filter("assigned_to_role", - :type => :list_optional, :values => role_values - ) unless role_values.empty? + :type => :list_optional, :values => lambda { Role.givable.collect {|r| [r.name, r.id.to_s] } } + ) add_available_filter "fixed_version_id", - :type => :list_optional, - :values => versions.sort.collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s] } + :type => :list_optional, :values => lambda { fixed_version_values } + + add_available_filter "fixed_version.due_date", + :type => :date, + :name => l(:label_attribute_of_fixed_version, :name => l(:field_effective_date)) + + add_available_filter "fixed_version.status", + :type => :list, + :name => l(:label_attribute_of_fixed_version, :name => l(:field_status)), + :values => Version::VERSION_STATUSES.map{|s| [l("version_status_#{s}"), s] } add_available_filter "category_id", :type => :list_optional, - :values => categories.collect{|s| [s.name, s.id.to_s] } + :values => lambda { project.issue_categories.collect{|s| [s.name, s.id.to_s] } } if project add_available_filter "subject", :type => :text add_available_filter "description", :type => :text @@ -224,22 +143,33 @@ class IssueQuery < Query :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]] end + add_available_filter "attachment", + :type => :text, :name => l(:label_attachment) + if User.current.logged? add_available_filter "watcher_id", :type => :list, :values => [["<< #{l(:label_me)} >>", "me"]] end - if subprojects.any? + add_available_filter("updated_by", + :type => :list, :values => lambda { author_values } + ) + + add_available_filter("last_updated_by", + :type => :list, :values => lambda { author_values } + ) + + if project && !project.leaf? add_available_filter "subproject_id", :type => :list_subprojects, - :values => subprojects.collect{|s| [s.name, s.id.to_s] } + :values => lambda { subproject_values } end add_custom_fields_filters(issue_custom_fields) add_associations_custom_fields_filters :project, :author, :assigned_to, :fixed_version IssueRelation::TYPES.each do |relation_type, options| - add_available_filter relation_type, :type => :relation, :label => options[:name] + add_available_filter relation_type, :type => :relation, :label => options[:name], :values => lambda {all_projects_values} end add_available_filter "parent_id", :type => :tree, :label => :field_parent_issue add_available_filter "child_id", :type => :tree, :label => :label_subtask_plural @@ -257,18 +187,29 @@ class IssueQuery < Query @available_columns += issue_custom_fields.visible.collect {|cf| QueryCustomFieldColumn.new(cf) } if User.current.allowed_to?(:view_time_entries, project, :global => true) + # insert the columns after total_estimated_hours or at the end index = @available_columns.find_index {|column| column.name == :total_estimated_hours} index = (index ? index + 1 : -1) - # insert the column after total_estimated_hours or at the end + + subselect = "SELECT SUM(hours) FROM #{TimeEntry.table_name}" + + " JOIN #{Project.table_name} ON #{Project.table_name}.id = #{TimeEntry.table_name}.project_id" + + " WHERE (#{TimeEntry.visible_condition(User.current)}) AND #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id" + @available_columns.insert index, QueryColumn.new(:spent_hours, - :sortable => "COALESCE((SELECT SUM(hours) FROM #{TimeEntry.table_name} WHERE #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id), 0)", + :sortable => "COALESCE((#{subselect}), 0)", :default_order => 'desc', :caption => :label_spent_time, :totalable => true ) + + subselect = "SELECT SUM(hours) FROM #{TimeEntry.table_name}" + + " JOIN #{Project.table_name} ON #{Project.table_name}.id = #{TimeEntry.table_name}.project_id" + + " JOIN #{Issue.table_name} subtasks ON subtasks.id = #{TimeEntry.table_name}.issue_id" + + " WHERE (#{TimeEntry.visible_condition(User.current)})" + + " AND subtasks.root_id = #{Issue.table_name}.root_id AND subtasks.lft >= #{Issue.table_name}.lft AND subtasks.rgt <= #{Issue.table_name}.rgt" + @available_columns.insert index+1, QueryColumn.new(:total_spent_hours, - :sortable => "COALESCE((SELECT SUM(hours) FROM #{TimeEntry.table_name} JOIN #{Issue.table_name} subtasks ON subtasks.id = #{TimeEntry.table_name}.issue_id" + - " WHERE subtasks.root_id = #{Issue.table_name}.root_id AND subtasks.lft >= #{Issue.table_name}.lft AND subtasks.rgt <= #{Issue.table_name}.rgt), 0)", + :sortable => "COALESCE((#{subselect}), 0)", :default_order => 'desc', :caption => :label_total_spent_time ) @@ -276,7 +217,7 @@ class IssueQuery < Query if User.current.allowed_to?(:set_issues_private, nil, :global => true) || User.current.allowed_to?(:set_own_issues_private, nil, :global => true) - @available_columns << QueryColumn.new(:is_private, :sortable => "#{Issue.table_name}.is_private") + @available_columns << QueryColumn.new(:is_private, :sortable => "#{Issue.table_name}.is_private", :groupable => true) end disabled_fields = Tracker.disabled_core_fields(trackers).map {|field| field.sub(/_id$/, '')} @@ -295,6 +236,14 @@ class IssueQuery < Query end end + def default_totalable_names + Setting.issue_list_default_totals.map(&:to_sym) + end + + def default_sort_criteria + [['id', 'desc']] + end + def base_scope Issue.visible.joins(:status, :project).where(statement) end @@ -306,13 +255,6 @@ class IssueQuery < Query raise StatementInvalid.new(e.message) end - # Returns the issue count by group or nil if query is not grouped - def issue_count_by_group - grouped_query do |scope| - scope.count - end - end - # Returns sum of all the issue's estimated_hours def total_for_estimated_hours(scope) map_total(scope.sum(:estimated_hours)) {|t| t.to_f.round(2)} @@ -320,25 +262,21 @@ class IssueQuery < Query # Returns sum of all the issue's time entries hours def total_for_spent_hours(scope) - total = if group_by_column.try(:name) == :project - # TODO: remove this when https://github.com/rails/rails/issues/21922 is fixed - # We have to do a custom join without the time_entries.project_id column - # that would trigger a ambiguous column name error - scope.joins("JOIN (SELECT issue_id, hours FROM #{TimeEntry.table_name}) AS joined_time_entries ON joined_time_entries.issue_id = #{Issue.table_name}.id"). - sum("joined_time_entries.hours") - else - scope.joins(:time_entries).sum("#{TimeEntry.table_name}.hours") - end + total = scope.joins(:time_entries). + where(TimeEntry.visible_condition(User.current)). + sum("#{TimeEntry.table_name}.hours") + map_total(total) {|t| t.to_f.round(2)} end # Returns the issues # Valid options are :order, :offset, :limit, :include, :conditions def issues(options={}) - order_option = [group_by_sort_order, options[:order]].flatten.reject(&:blank?) + order_option = [group_by_sort_order, (options[:order] || sort_clause)].flatten.reject(&:blank?) scope = Issue.visible. joins(:status, :project). + preload(:priority). where(statement). includes(([:status, :project] + (options[:include] || [])).uniq). where(options[:conditions]). @@ -347,9 +285,9 @@ class IssueQuery < Query limit(options[:limit]). offset(options[:offset]) - scope = scope.preload(:custom_values) - if has_column?(:author) - scope = scope.preload(:author) + scope = scope.preload([:tracker, :author, :assigned_to, :fixed_version, :category, :attachments] & columns.map(&:name)) + if has_custom_field_column? + scope = scope.preload(:custom_values) end issues = scope.to_a @@ -360,9 +298,15 @@ class IssueQuery < Query if has_column?(:total_spent_hours) Issue.load_visible_total_spent_hours(issues) end + if has_column?(:last_updated_by) + Issue.load_visible_last_updated_by(issues) + end if has_column?(:relations) Issue.load_visible_relations(issues) end + if has_column?(:last_notes) + Issue.load_visible_last_notes(issues) + end issues rescue ::ActiveRecord::StatementInvalid => e raise StatementInvalid.new(e.message) @@ -370,7 +314,7 @@ class IssueQuery < Query # Returns the issues ids def issue_ids(options={}) - order_option = [group_by_sort_order, options[:order]].flatten.reject(&:blank?) + order_option = [group_by_sort_order, (options[:order] || sort_clause)].flatten.reject(&:blank?) Issue.visible. joins(:status, :project). @@ -415,6 +359,27 @@ class IssueQuery < Query raise StatementInvalid.new(e.message) end + def sql_for_updated_by_field(field, operator, value) + neg = (operator == '!' ? 'NOT' : '') + subquery = "SELECT 1 FROM #{Journal.table_name}" + + " WHERE #{Journal.table_name}.journalized_type='Issue' AND #{Journal.table_name}.journalized_id=#{Issue.table_name}.id" + + " AND (#{sql_for_field field, '=', value, Journal.table_name, 'user_id'})" + + " AND (#{Journal.visible_notes_condition(User.current, :skip_pre_condition => true)})" + + "#{neg} EXISTS (#{subquery})" + end + + def sql_for_last_updated_by_field(field, operator, value) + neg = (operator == '!' ? 'NOT' : '') + 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'})" + + " AND sj.id = (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" + + " AND (#{Journal.visible_notes_condition(User.current, :skip_pre_condition => true)}))" + + "#{neg} EXISTS (#{subquery})" + end + def sql_for_watcher_id_field(field, operator, value) db_table = Watcher.table_name @@ -477,6 +442,22 @@ class IssueQuery < Query end end + def sql_for_fixed_version_status_field(field, operator, value) + where = sql_for_field(field, operator, value, Version.table_name, "status") + version_ids = versions(:conditions => [where]).map(&:id) + + nl = operator == "!" ? "#{Issue.table_name}.fixed_version_id IS NULL OR" : '' + "(#{nl} #{sql_for_field("fixed_version_id", "=", version_ids, Issue.table_name, "fixed_version_id")})" + end + + def sql_for_fixed_version_due_date_field(field, operator, value) + where = sql_for_field(field, operator, value, Version.table_name, "effective_date") + version_ids = versions(:conditions => [where]).map(&:id) + + nl = operator == "!*" ? "#{Issue.table_name}.fixed_version_id IS NULL OR" : '' + "(#{nl} #{sql_for_field("fixed_version_id", "=", version_ids, Issue.table_name, "fixed_version_id")})" + end + def sql_for_is_private_field(field, operator, value) op = (operator == "=" ? 'IN' : 'NOT IN') va = value.map {|v| v == '0' ? self.class.connection.quoted_false : self.class.connection.quoted_true}.uniq.join(',') @@ -484,6 +465,18 @@ class IssueQuery < Query "#{Issue.table_name}.is_private #{op} (#{va})" end + def sql_for_attachment_field(field, operator, value) + case operator + when "*", "!*" + e = (operator == "*" ? "EXISTS" : "NOT EXISTS") + "#{e} (SELECT 1 FROM #{Attachment.table_name} a WHERE a.container_type = 'Issue' AND a.container_id = #{Issue.table_name}.id)" + when "~", "!~" + c = sql_contains("a.filename", value.first) + e = (operator == "~" ? "EXISTS" : "NOT EXISTS") + "#{e} (SELECT 1 FROM #{Attachment.table_name} a WHERE a.container_type = 'Issue' AND a.container_id = #{Issue.table_name}.id AND #{c})" + end + end + def sql_for_parent_id_field(field, operator, value) case operator when "=" @@ -525,6 +518,17 @@ class IssueQuery < Query end end + def sql_for_updated_on_field(field, operator, value) + case operator + when "!*" + "#{Issue.table_name}.updated_on = #{Issue.table_name}.created_on" + when "*" + "#{Issue.table_name}.updated_on > #{Issue.table_name}.created_on" + else + sql_for_field("updated_on", operator, value, Issue.table_name, "updated_on") + end + end + def sql_for_issue_id_field(field, operator, value) if operator == "=" # accepts a comma separated list of ids @@ -581,4 +585,36 @@ class IssueQuery < Query IssueRelation::TYPES.keys.each do |relation_type| alias_method "sql_for_#{relation_type}_field".to_sym, :sql_for_relations end + + def joins_for_order_statement(order_options) + joins = [super] + + if order_options + if order_options.include?('authors') + joins << "LEFT OUTER JOIN #{User.table_name} authors ON authors.id = #{queried_table_name}.author_id" + end + if order_options.include?('users') + joins << "LEFT OUTER JOIN #{User.table_name} ON #{User.table_name}.id = #{queried_table_name}.assigned_to_id" + end + if order_options.include?('last_journal_user') + joins << "LEFT OUTER JOIN #{Journal.table_name} ON #{Journal.table_name}.id = (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 AND #{Journal.visible_notes_condition(User.current, :skip_pre_condition => true)})" + + " LEFT OUTER JOIN #{User.table_name} last_journal_user ON last_journal_user.id = #{Journal.table_name}.user_id"; + end + if order_options.include?('versions') + joins << "LEFT OUTER JOIN #{Version.table_name} ON #{Version.table_name}.id = #{queried_table_name}.fixed_version_id" + end + if order_options.include?('issue_categories') + joins << "LEFT OUTER JOIN #{IssueCategory.table_name} ON #{IssueCategory.table_name}.id = #{queried_table_name}.category_id" + end + if order_options.include?('trackers') + joins << "LEFT OUTER JOIN #{Tracker.table_name} ON #{Tracker.table_name}.id = #{queried_table_name}.tracker_id" + end + if order_options.include?('enumerations') + joins << "LEFT OUTER JOIN #{IssuePriority.table_name} ON #{IssuePriority.table_name}.id = #{queried_table_name}.priority_id" + end + end + + joins.any? ? joins.join(' ') : nil + end end diff --git a/app/models/issue_relation.rb b/app/models/issue_relation.rb index 7e92712ed..f23f74478 100644 --- a/app/models/issue_relation.rb +++ b/app/models/issue_relation.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -91,7 +91,7 @@ class IssueRelation < ActiveRecord::Base self.issue_to = Issue.visible(user).find_by_id(issue_id) end end - + super(attrs) end diff --git a/app/models/issue_status.rb b/app/models/issue_status.rb index 31c0f031c..6d29cfc8d 100644 --- a/app/models/issue_status.rb +++ b/app/models/issue_status.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,6 +16,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class IssueStatus < ActiveRecord::Base + include Redmine::SafeAttributes + before_destroy :check_integrity has_many :workflows, :class_name => 'WorkflowTransition', :foreign_key => "old_status_id" has_many :workflow_transitions_as_new_status, :class_name => 'WorkflowTransition', :foreign_key => "new_status_id" @@ -33,6 +35,11 @@ class IssueStatus < ActiveRecord::Base scope :sorted, lambda { order(:position) } scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)} + safe_attributes 'name', + 'is_closed', + 'position', + 'default_done_ratio' + # Update all the +Issues+ setting their done_ratio to the value of their +IssueStatus+ def self.update_issue_done_ratios if Issue.use_status_for_done_ratio? @@ -66,7 +73,7 @@ class IssueStatus < ActiveRecord::Base end end - scope.uniq.to_a.sort + scope.distinct.to_a.sort else [] end @@ -85,12 +92,13 @@ class IssueStatus < ActiveRecord::Base if is_closed_changed? && is_closed == true # First we update issues that have a journal for when the current status was set, # a subselect is used to update all issues with a single query - subselect = "SELECT MAX(j.created_on) FROM #{Journal.table_name} j" + - " JOIN #{JournalDetail.table_name} d ON d.journal_id = j.id" + - " WHERE j.journalized_type = 'Issue' AND j.journalized_id = #{Issue.table_name}.id" + - " AND d.property = 'attr' AND d.prop_key = 'status_id' AND d.value = :status_id" - Issue.where(:status_id => id, :closed_on => nil). - update_all(["closed_on = (#{subselect})", {:status_id => id.to_s}]) + subquery = Journal.joins(:details). + where(:journalized_type => 'Issue'). + where("journalized_id = #{Issue.table_name}.id"). + where(:journal_details => {:property => 'attr', :prop_key => 'status_id', :value => id.to_s}). + select("MAX(created_on)"). + to_sql + Issue.where(:status_id => id, :closed_on => nil).update_all("closed_on = (#{subquery})") # Then we update issues that don't have a journal which means the # current status was set on creation @@ -108,6 +116,7 @@ class IssueStatus < ActiveRecord::Base # Deletes associated workflows def delete_workflow_rules - WorkflowRule.delete_all(["old_status_id = :id OR new_status_id = :id", {:id => id}]) + WorkflowRule.where(:old_status_id => id).delete_all + WorkflowRule.where(:new_status_id => id).delete_all end end diff --git a/app/models/journal.rb b/app/models/journal.rb index 927f86f65..0d2e479ca 100644 --- a/app/models/journal.rb +++ b/app/models/journal.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,6 +16,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class Journal < ActiveRecord::Base + include Redmine::SafeAttributes + belongs_to :journalized, :polymorphic => true # added as a quick fix to allow eager loading of the polymorphic association # since always associated to an issue, for now @@ -38,18 +40,31 @@ class Journal < ActiveRecord::Base :scope => preload({:issue => :project}, :user). joins("LEFT OUTER JOIN #{JournalDetail.table_name} ON #{JournalDetail.table_name}.journal_id = #{Journal.table_name}.id"). where("#{Journal.table_name}.journalized_type = 'Issue' AND" + - " (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')").uniq + " (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')").distinct before_create :split_private_notes - after_create :send_notification + after_commit :send_notification, :on => :create scope :visible, lambda {|*args| user = args.shift || User.current + options = args.shift || {} + joins(:issue => :project). - where(Issue.visible_condition(user, *args)). - where("(#{Journal.table_name}.private_notes = ? OR (#{Project.allowed_to_condition(user, :view_private_notes, *args)}))", false) + where(Issue.visible_condition(user, options)). + where(Journal.visible_notes_condition(user, :skip_pre_condition => true)) } + safe_attributes 'notes', + :if => lambda {|journal, user| journal.new_record? || journal.editable_by?(user)} + safe_attributes 'private_notes', + :if => lambda {|journal, user| user.allowed_to?(:set_notes_private, journal.project)} + + # Returns a SQL condition to filter out journals with notes that are not visible to user + def self.visible_notes_condition(user=User.current, options={}) + private_notes_permission = Project.allowed_to_condition(user, :view_private_notes, options) + sanitize_sql_for_conditions(["(#{table_name}.private_notes = ? OR #{table_name}.user_id = ? OR (#{private_notes_permission}))", false, user.id]) + end + def initialize(*args) super if journalized @@ -118,7 +133,7 @@ class Journal < ActiveRecord::Base end def attachments - journalized.respond_to?(:attachments) ? journalized.attachments : nil + journalized.respond_to?(:attachments) ? journalized.attachments : [] end # Returns a string of css classes @@ -219,18 +234,26 @@ class Journal < ActiveRecord::Base def journalize_changes # attributes changes if @attributes_before_change - journalized.journalized_attribute_names.each {|attribute| + attrs = (journalized.journalized_attribute_names + @attributes_before_change.keys).uniq + attrs.each do |attribute| before = @attributes_before_change[attribute] after = journalized.send(attribute) next if before == after || (before.blank? && after.blank?) add_attribute_detail(attribute, before, after) - } + end end + # custom fields changes if @custom_values_before_change - # custom fields changes - journalized.custom_field_values.each {|c| - before = @custom_values_before_change[c.custom_field_id] - after = c.value + values_by_custom_field_id = {} + @custom_values_before_change.each do |custom_field_id, value| + values_by_custom_field_id[custom_field_id] = nil + end + journalized.custom_field_values.each do |c| + values_by_custom_field_id[c.custom_field_id] = c.value + end + + values_by_custom_field_id.each do |custom_field_id, after| + before = @custom_values_before_change[custom_field_id] next if before == after || (before.blank? && after.blank?) if before.is_a?(Array) || after.is_a?(Array) @@ -239,16 +262,16 @@ class Journal < ActiveRecord::Base # values removed (before - after).reject(&:blank?).each do |value| - add_custom_value_detail(c, value, nil) + add_custom_field_detail(custom_field_id, value, nil) end # values added (after - before).reject(&:blank?).each do |value| - add_custom_value_detail(c, nil, value) + add_custom_field_detail(custom_field_id, nil, value) end else - add_custom_value_detail(c, before, after) + add_custom_field_detail(custom_field_id, before, after) end - } + end end start end @@ -259,8 +282,8 @@ class Journal < ActiveRecord::Base end # Adds a journal detail for a custom field value change - def add_custom_value_detail(custom_value, old_value, value) - add_detail('cf', custom_value.custom_field_id, old_value, value) + def add_custom_field_detail(custom_field_id, old_value, value) + add_detail('cf', custom_field_id, old_value, value) end # Adds a journal detail diff --git a/app/models/journal_detail.rb b/app/models/journal_detail.rb index f7cea7909..f901d2702 100644 --- a/app/models/journal_detail.rb +++ b/app/models/journal_detail.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/mail_handler.rb b/app/models/mail_handler.rb old mode 100644 new mode 100755 index fef2beb8b..aa0ea1e45 --- a/app/models/mail_handler.rb +++ b/app/models/mail_handler.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -130,7 +130,7 @@ class MailHandler < ActionMailer::Base end add_user_to_group(handler_options[:default_group]) unless handler_options[:no_account_notice] - Mailer.account_information(@user, @user.password).deliver + ::Mailer.account_information(@user, @user.password).deliver end else if logger @@ -446,19 +446,27 @@ class MailHandler < ActionMailer::Base def plain_text_body return @plain_text_body unless @plain_text_body.nil? - parts = if (text_parts = email.all_parts.select {|p| p.mime_type == 'text/plain'}).present? - text_parts - elsif (html_parts = email.all_parts.select {|p| p.mime_type == 'text/html'}).present? - html_parts - else - [email] - end + # check if we have any plain-text parts with content + @plain_text_body = email_parts_to_text(email.all_parts.select {|p| p.mime_type == 'text/plain'}).presence + # if not, we try to parse the body from the HTML-parts + @plain_text_body ||= email_parts_to_text(email.all_parts.select {|p| p.mime_type == 'text/html'}).presence + + # If there is still no body found, and there are no mime-parts defined, + # we use the whole raw mail body + @plain_text_body ||= email_parts_to_text([email]).presence if email.all_parts.empty? + + # As a fallback we return an empty plain text body (e.g. if we have only + # empty text parts but a non-text attachment) + @plain_text_body ||= "" + end + + def email_parts_to_text(parts) parts.reject! do |part| part.attachment? end - @plain_text_body = parts.map do |p| + parts.map do |p| body_charset = Mail::RubyVer.respond_to?(:pick_encoding) ? Mail::RubyVer.pick_encoding(p.charset).to_s : p.charset @@ -466,8 +474,6 @@ class MailHandler < ActionMailer::Base # convert html parts to text p.mime_type == 'text/html' ? self.class.html_body_to_text(body) : self.class.plain_text_body_to_text(body) end.join("\r\n") - - @plain_text_body end def cleaned_up_text_body @@ -562,9 +568,18 @@ class MailHandler < ActionMailer::Base # Removes the email body of text after the truncation configurations. def cleanup_body(body) - delimiters = Setting.mail_handler_body_delimiters.to_s.split(/[\r\n]+/).reject(&:blank?).map {|s| Regexp.escape(s)} + delimiters = Setting.mail_handler_body_delimiters.to_s.split(/[\r\n]+/).reject(&:blank?) + + if Setting.mail_handler_enable_regex_delimiters? + begin + delimiters = delimiters.map {|s| Regexp.new(s)} + rescue RegexpError => e + logger.error "MailHandler: invalid regexp delimiter found in mail_handler_body_delimiters setting (#{e.message})" if logger + end + end + unless delimiters.empty? - regex = Regexp.new("^[> ]*(#{ delimiters.join('|') })\s*[\r\n].*", Regexp::MULTILINE) + regex = Regexp.new("^[> ]*(#{ Regexp.union(delimiters) })[[:blank:]]*[\r\n].*", Regexp::MULTILINE) body = body.gsub(regex, '') end body.strip diff --git a/app/models/mailer.rb b/app/models/mailer.rb index 1de0b9081..0c8c55c7e 100644 --- a/app/models/mailer.rb +++ b/app/models/mailer.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -61,7 +61,7 @@ class Mailer < ActionMailer::Base to = issue.notified_users cc = issue.notified_watchers - to issue.each_notification(to + cc) do |users| - Mailer.issue_add(issue, to & users, cc & users).deliver + issue_add(issue, to & users, cc & users).deliver end end @@ -95,7 +95,7 @@ class Mailer < ActionMailer::Base cc = journal.notified_watchers - to journal.each_notification(to + cc) do |users| issue.each_notification(users) do |users2| - Mailer.issue_edit(journal, to & users2, cc & users2).deliver + issue_edit(journal, to & users2, cc & users2).deliver end end end @@ -317,7 +317,7 @@ class Mailer < ActionMailer::Base # TODO: maybe not the best way to handle this return if user.admin? && user.login == 'admin' && user.mail == 'admin@example.net' - Mailer.security_notification(user, + security_notification(user, message: :mail_body_password_updated, title: :button_change_password, remote_ip: options[:remote_ip], @@ -364,7 +364,7 @@ class Mailer < ActionMailer::Base return unless changes.present? users = User.active.where(admin: true).to_a - Mailer.settings_updated(users, changes).deliver + settings_updated(users, changes).deliver end def test_email(user) diff --git a/app/models/member.rb b/app/models/member.rb index 389684dab..0e8fb1d47 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -19,7 +19,7 @@ class Member < ActiveRecord::Base belongs_to :user belongs_to :principal, :foreign_key => 'user_id' has_many :member_roles, :dependent => :destroy - has_many :roles, lambda {uniq}, :through => :member_roles + has_many :roles, lambda { distinct }, :through => :member_roles belongs_to :project validates_presence_of :principal, :project @@ -27,10 +27,21 @@ class Member < ActiveRecord::Base validate :validate_role attr_protected :id - before_destroy :set_issue_category_nil + before_destroy :set_issue_category_nil, :remove_from_project_default_assigned_to scope :active, lambda { joins(:principal).where(:users => {:status => Principal::STATUS_ACTIVE})} + # Sort by first role and principal + scope :sorted, lambda { + includes(:member_roles, :roles, :principal). + reorder("#{Role.table_name}.position"). + order(Principal.fields_for_order_statement) + } + scope :sorted_by_project, lambda { + includes(:project). + reorder("#{Project.table_name}.lft") + } + alias :base_reload :reload def reload(*args) @managed_roles = nil @@ -128,7 +139,7 @@ class Member < ActiveRecord::Base if principal.is_a?(Group) !user.nil? && user.groups.include?(principal) else - self.user == user + self.principal == user end end @@ -140,6 +151,13 @@ class Member < ActiveRecord::Base end end + def remove_from_project_default_assigned_to + if user_id && project && project.default_assigned_to_id == user_id + # remove project based auto assignments for this member + project.update_column(:default_assigned_to_id, nil) + end + end + # Returns the roles that the member is allowed to manage # in the project the member belongs to def managed_roles @@ -161,7 +179,8 @@ class Member < ActiveRecord::Base end end - # Creates memberships for principal with the attributes + # Creates memberships for principal with the attributes, or add the roles + # if the membership already exists. # * project_ids : one or more project ids # * role_ids : ids of the roles to give to each membership # @@ -171,16 +190,18 @@ class Member < ActiveRecord::Base members = [] if attributes project_ids = Array.wrap(attributes[:project_ids] || attributes[:project_id]) - role_ids = attributes[:role_ids] + role_ids = Array.wrap(attributes[:role_ids]) project_ids.each do |project_id| - members << Member.new(:principal => principal, :role_ids => role_ids, :project_id => project_id) + member = Member.find_or_new(project_id, principal) + member.role_ids |= role_ids + member.save + members << member end - principal.members << members end members end - # Finds or initilizes a Member for the given project and principal + # Finds or initializes a Member for the given project and principal def self.find_or_new(project, principal) project_id = project.is_a?(Project) ? project.id : project principal_id = principal.is_a?(Principal) ? principal.id : principal @@ -193,6 +214,6 @@ class Member < ActiveRecord::Base protected def validate_role - errors.add_on_empty :role if member_roles.empty? && roles.empty? + errors.add(:role, :empty) if member_roles.empty? && roles.empty? end end diff --git a/app/models/member_role.rb b/app/models/member_role.rb index d65e7b606..508475948 100644 --- a/app/models/member_role.rb +++ b/app/models/member_role.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -72,9 +72,6 @@ class MemberRole < ActiveRecord::Base end def remove_inherited_roles - MemberRole.where(:inherited_from => id).group_by(&:member). - each do |member, member_roles| - member_roles.each(&:destroy) - end + MemberRole.where(:inherited_from => id).destroy_all end end diff --git a/app/models/message.rb b/app/models/message.rb index cf13ca4a5..65ae3148a 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/news.rb b/app/models/news.rb index b5a3924b7..7d900d331 100644 --- a/app/models/news.rb +++ b/app/models/news.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/principal.rb b/app/models/principal.rb index f3e0a3d4d..e4410dde2 100644 --- a/app/models/principal.rb +++ b/app/models/principal.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -28,9 +28,7 @@ class Principal < ActiveRecord::Base has_many :members, :foreign_key => 'user_id', :dependent => :destroy has_many :memberships, - lambda {preload(:project, :roles). - joins(:project). - where("#{Project.table_name}.status<>#{Project::STATUS_ARCHIVED}")}, + lambda {joins(:project).where.not(:projects => {:status => Project::STATUS_ARCHIVED})}, :class_name => 'Member', :foreign_key => 'user_id' has_many :projects, :through => :memberships @@ -107,6 +105,12 @@ class Principal < ActiveRecord::Base scope :sorted, lambda { order(*Principal.fields_for_order_statement)} before_create :set_default_empty_values + before_destroy :nullify_projects_default_assigned_to + + def reload(*args) + @project_ids = nil + super + end def name(formatter = nil) to_s @@ -124,9 +128,14 @@ class Principal < ActiveRecord::Base Principal.visible(user).where(:id => id).first == self end - # Return true if the principal is a member of project + # Returns true if the principal is a member of project def member_of?(project) - projects.to_a.include?(project) + project.is_a?(Project) && project_ids.include?(project.id) + end + + # Returns an array of the project ids that the principal is a member of + def project_ids + @project_ids ||= super.freeze end def <=>(principal) @@ -172,6 +181,10 @@ class Principal < ActiveRecord::Base principal end + def nullify_projects_default_assigned_to + Project.where(default_assigned_to: self).update_all(default_assigned_to_id: nil) + end + protected # Make sure we don't try to insert NULL values (see #4632) diff --git a/app/models/project.rb b/app/models/project.rb index 60143ea8f..63eaf0077 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -37,21 +37,22 @@ class Project < ActiveRecord::Base has_and_belongs_to_many :trackers, lambda {order(:position)} has_many :issues, :dependent => :destroy has_many :issue_changes, :through => :issues, :source => :journals - has_many :versions, lambda {order("#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC")}, :dependent => :destroy + has_many :versions, :dependent => :destroy belongs_to :default_version, :class_name => 'Version' + belongs_to :default_assigned_to, :class_name => 'Principal' has_many :time_entries, :dependent => :destroy - has_many :queries, :class_name => 'IssueQuery', :dependent => :delete_all + has_many :queries, :dependent => :delete_all has_many :documents, :dependent => :destroy has_many :news, lambda {includes(:author)}, :dependent => :destroy - has_many :issue_categories, lambda {order("#{IssueCategory.table_name}.name")}, :dependent => :delete_all - has_many :boards, lambda {order("position ASC")}, :dependent => :destroy - has_one :repository, lambda {where(["is_default = ?", true])} + has_many :issue_categories, lambda {order(:name)}, :dependent => :delete_all + has_many :boards, lambda {order(:position)}, :inverse_of => :project, :dependent => :destroy + has_one :repository, lambda {where(:is_default => true)} has_many :repositories, :dependent => :destroy has_many :changesets, :through => :repository has_one :wiki, :dependent => :destroy # Custom field for the project issues has_and_belongs_to_many :issue_custom_fields, - lambda {order("#{CustomField.table_name}.position")}, + lambda {order(:position)}, :class_name => 'IssueCustomField', :join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}", :association_foreign_key => 'custom_field_id' @@ -72,7 +73,7 @@ class Project < ActiveRecord::Base validates_uniqueness_of :identifier, :if => Proc.new {|p| p.identifier_changed?} validates_length_of :name, :maximum => 255 validates_length_of :homepage, :maximum => 255 - validates_length_of :identifier, :in => 1..IDENTIFIER_MAX_LENGTH + validates_length_of :identifier, :maximum => IDENTIFIER_MAX_LENGTH # downcase letters, digits, dashes but not digits only validates_format_of :identifier, :with => /\A(?!\d+$)[a-z0-9\-_]*\z/, :if => Proc.new { |p| p.identifier_changed? } # reserved words @@ -103,11 +104,9 @@ class Project < ActiveRecord::Base where(Project.allowed_to_condition(user, permission, *args)) } scope :like, lambda {|arg| - if arg.blank? - where(nil) - else - pattern = "%#{arg.to_s.strip.downcase}%" - where("LOWER(identifier) LIKE :p OR LOWER(name) LIKE :p", :p => pattern) + if arg.present? + pattern = "%#{arg.to_s.strip}%" + where("LOWER(identifier) LIKE LOWER(:p) OR LOWER(name) LIKE LOWER(:p)", :p => pattern) end } scope :sorted, lambda {order(:lft)} @@ -173,15 +172,16 @@ class Project < ActiveRecord::Base # Returns a SQL conditions string used to find all projects for which +user+ has the given +permission+ # # Valid options: - # * :project => limit the condition to project - # * :with_subprojects => limit the condition to project and its subprojects - # * :member => limit the condition to the user projects + # * :skip_pre_condition => true don't check that the module is enabled (eg. when the condition is already set elsewhere in the query) + # * :project => project limit the condition to project + # * :with_subprojects => true limit the condition to project and its subprojects + # * :member => true limit the condition to the user projects def self.allowed_to_condition(user, permission, options={}) perm = Redmine::AccessControl.permission(permission) base_statement = (perm && perm.read? ? "#{Project.table_name}.status <> #{Project::STATUS_ARCHIVED}" : "#{Project.table_name}.status = #{Project::STATUS_ACTIVE}") - if perm && perm.project_module + if !options[:skip_pre_condition] && perm && perm.project_module # If the permission belongs to a project module, make sure the module is enabled - base_statement << " AND #{Project.table_name}.id IN (SELECT em.project_id FROM #{EnabledModule.table_name} em WHERE em.name='#{perm.project_module}')" + base_statement << " AND EXISTS (SELECT 1 AS one FROM #{EnabledModule.table_name} em WHERE em.project_id = #{Project.table_name}.id AND em.name='#{perm.project_module}')" end if project = options[:project] project_statement = project.project_condition(options[:with_subprojects]) @@ -204,9 +204,9 @@ class Project < ActiveRecord::Base statement_by_role[role] = s end end - user.projects_by_role.each do |role, projects| - if role.allowed_to?(permission) && projects.any? - statement_by_role[role] = "#{Project.table_name}.id IN (#{projects.collect(&:id).join(',')})" + user.project_ids_by_role.each do |role, project_ids| + if role.allowed_to?(permission) && project_ids.any? + statement_by_role[role] = "#{Project.table_name}.id IN (#{project_ids.join(',')})" end end if statement_by_role.empty? @@ -235,11 +235,11 @@ class Project < ActiveRecord::Base end def principals - @principals ||= Principal.active.joins(:members).where("#{Member.table_name}.project_id = ?", id).uniq + @principals ||= Principal.active.joins(:members).where("#{Member.table_name}.project_id = ?", id).distinct end def users - @users ||= User.active.joins(:members).where("#{Member.table_name}.project_id = ?", id).uniq + @users ||= User.active.joins(:members).where("#{Member.table_name}.project_id = ?", id).distinct end # Returns the Systemwide and project specific activities @@ -324,6 +324,7 @@ class Project < ActiveRecord::Base @shared_versions = nil @rolled_up_versions = nil @rolled_up_trackers = nil + @rolled_up_statuses = nil @rolled_up_custom_fields = nil @all_issue_custom_fields = nil @all_time_entry_custom_fields = nil @@ -367,7 +368,7 @@ class Project < ActiveRecord::Base if version_ids.any? && Issue. - includes(:project). + joins(:project). where("#{Project.table_name}.lft < ? OR #{Project.table_name}.rgt > ?", lft, rgt). where(:fixed_version_id => version_ids). exists? @@ -448,10 +449,21 @@ class Project < ActiveRecord::Base joins(projects: :enabled_modules). where("#{Project.table_name}.status <> ?", STATUS_ARCHIVED). where(:enabled_modules => {:name => 'issue_tracking'}). - uniq. + distinct. sorted end + def rolled_up_statuses + issue_status_ids = WorkflowTransition. + where(:tracker_id => rolled_up_trackers.map(&:id)). + distinct. + pluck(:old_status_id, :new_status_id). + flatten. + uniq + + IssueStatus.where(:id => issue_status_ids).sorted + end + # Closes open and locked project versions that are completed def close_completed_versions Version.transaction do @@ -509,17 +521,23 @@ class Project < ActiveRecord::Base # Adds user as a project member with the default role # Used for when a non-admin user creates a project def add_default_member(user) - role = Role.givable.find_by_id(Setting.new_project_user_role_id.to_i) || Role.givable.first + role = self.class.default_member_role member = Member.new(:project => self, :principal => user, :roles => [role]) self.members << member member end + # Default role that is given to non-admin users that + # create a project + def self.default_member_role + Role.givable.find_by_id(Setting.new_project_user_role_id.to_i) || Role.givable.first + end + # Deletes all project's members def delete_all_members me, mr = Member.table_name, MemberRole.table_name self.class.connection.delete("DELETE FROM #{mr} WHERE #{mr}.member_id IN (SELECT #{me}.id FROM #{me} WHERE #{me}.project_id = #{id})") - Member.delete_all(['project_id = ?', id]) + Member.where(:project_id => id).delete_all end # Return a Principal scope of users/groups issues can be assigned to @@ -533,7 +551,7 @@ class Project < ActiveRecord::Base active. joins(:members => :roles). where(:type => types, :members => {:project_id => id}, :roles => {:assignable => true}). - uniq. + distinct. sorted if tracker @@ -738,10 +756,21 @@ class Project < ActiveRecord::Base 'tracker_ids', 'issue_custom_field_ids', 'parent_id', - 'default_version_id' + 'default_version_id', + 'default_assigned_to_id' safe_attributes 'enabled_module_names', - :if => lambda {|project, user| project.new_record? || user.allowed_to?(:select_project_modules, project) } + :if => lambda {|project, user| + if project.new_record? + if user.admin? + true + else + default_member_role.has_permission?(:select_project_modules) + end + else + user.allowed_to?(:select_project_modules, project) + end + } safe_attributes 'inherit_members', :if => lambda {|project, user| project.parent.nil? || project.parent.visible?(user)} @@ -789,7 +818,7 @@ class Project < ActiveRecord::Base def copy(project, options={}) project = project.is_a?(Project) ? project : Project.find(project) - to_be_copied = %w(wiki versions issue_categories issues members queries boards) + to_be_copied = %w(members wiki versions issue_categories issues queries boards) to_be_copied = to_be_copied & Array.wrap(options[:only]) unless options[:only].nil? Project.transaction do @@ -825,8 +854,11 @@ class Project < ActiveRecord::Base end # Yields the given block for each project with its level in the tree - def self.project_tree(projects, &block) + def self.project_tree(projects, options={}, &block) ancestors = [] + if options[:init_level] && projects.first + ancestors = projects.first.ancestors.to_a + end projects.sort_by(&:lft).each do |project| while (ancestors.any? && !project.is_descendant_of?(ancestors.last)) ancestors.pop @@ -850,7 +882,7 @@ class Project < ActiveRecord::Base end def remove_inherited_member_roles - member_roles = memberships.map(&:member_roles).flatten + member_roles = MemberRole.where(:member_id => membership_ids).to_a member_role_ids = member_roles.map(&:id) member_roles.each do |member_role| if member_role.inherited_from && !member_role_ids.include?(member_role.inherited_from) @@ -949,7 +981,7 @@ class Project < ActiveRecord::Base # get copied before their children project.issues.reorder('root_id, lft').each do |issue| new_issue = Issue.new - new_issue.copy_from(issue, :subtasks => false, :link => false) + new_issue.copy_from(issue, :subtasks => false, :link => false, :keep_status => true) new_issue.project = self # Changing project resets the custom field values # TODO: handle this in Issue#project= @@ -1052,12 +1084,12 @@ class Project < ActiveRecord::Base # Copies queries from +project+ def copy_queries(project) project.queries.each do |query| - new_query = IssueQuery.new + new_query = query.class.new new_query.attributes = query.attributes.dup.except("id", "project_id", "sort_criteria", "user_id", "type") new_query.sort_criteria = query.sort_criteria if query.sort_criteria new_query.project = self new_query.user_id = query.user_id - new_query.role_ids = query.role_ids if query.visibility == IssueQuery::VISIBILITY_ROLES + new_query.role_ids = query.role_ids if query.visibility == ::Query::VISIBILITY_ROLES self.queries << new_query end end diff --git a/app/models/project_custom_field.rb b/app/models/project_custom_field.rb index 2db407099..54e7958a3 100644 --- a/app/models/project_custom_field.rb +++ b/app/models/project_custom_field.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/query.rb b/app/models/query.rb index a6d32f102..c8fc7789c 100644 --- a/app/models/query.rb +++ b/app/models/query.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -74,13 +74,33 @@ class QueryColumn end end +class QueryAssociationColumn < QueryColumn + + def initialize(association, attribute, options={}) + @association = association + @attribute = attribute + name_with_assoc = "#{association}.#{attribute}".to_sym + super(name_with_assoc, options) + end + + def value_object(object) + if assoc = object.send(@association) + assoc.send @attribute + end + end + + def css_classes + @css_classes ||= "#{@association}-#{@attribute}" + end +end + class QueryCustomFieldColumn < QueryColumn - def initialize(custom_field) + def initialize(custom_field, options={}) self.name = "cf_#{custom_field.id}".to_sym self.sortable = custom_field.order_statement || false self.groupable = custom_field.group_statement || false - self.totalable = custom_field.totalable? + self.totalable = options.key?(:totalable) ? !!options[:totalable] : custom_field.totalable? @inline = true @cf = custom_field end @@ -120,8 +140,8 @@ end class QueryAssociationCustomFieldColumn < QueryCustomFieldColumn - def initialize(association, custom_field) - super(custom_field) + def initialize(association, custom_field, options={}) + super(custom_field, options) self.name = "#{association}.cf_#{custom_field.id}".to_sym # TODO: support sorting/grouping by association custom field self.sortable = false @@ -140,10 +160,46 @@ class QueryAssociationCustomFieldColumn < QueryCustomFieldColumn end end +class QueryFilter + include Redmine::I18n + + def initialize(field, options) + @field = field.to_s + @options = options + @options[:name] ||= l(options[:label] || "field_#{field}".gsub(/_id$/, '')) + # Consider filters with a Proc for values as remote by default + @remote = options.key?(:remote) ? options[:remote] : options[:values].is_a?(Proc) + end + + def [](arg) + if arg == :values + values + else + @options[arg] + end + end + + def values + @values ||= begin + values = @options[:values] + if values.is_a?(Proc) + values = values.call + end + values + end + end + + def remote + @remote + end +end + class Query < ActiveRecord::Base class StatementInvalid < ::ActiveRecord::StatementInvalid end + include Redmine::SubclassFactory + VISIBILITY_PRIVATE = 0 VISIBILITY_ROLES = 1 VISIBILITY_PUBLIC = 2 @@ -213,7 +269,7 @@ class Query < ActiveRecord::Base :list => [ "=", "!" ], :list_status => [ "o", "=", "!", "c", "*" ], :list_optional => [ "=", "!", "!*", "*" ], - :list_subprojects => [ "*", "!*", "=" ], + :list_subprojects => [ "*", "!*", "=", "!" ], :date => [ "=", ">=", "<=", "><", "t+", ">t-", " [ "=", ">=", "<=", "><", ">t-", " [ "=", "~", "!", "!~", "!*", "*" ], @@ -229,6 +285,77 @@ class Query < ActiveRecord::Base class_attribute :queried_class + # Permission required to view the queries, set on subclasses. + class_attribute :view_permission + + # Scope of queries that are global or on the given project + scope :global_or_on_project, lambda {|project| + where(:project_id => (project.nil? ? nil : [nil, project.id])) + } + + scope :sorted, lambda {order(:name, :id)} + + # Scope of visible queries, can be used from subclasses only. + # Unlike other visible scopes, a class methods is used as it + # let handle inheritance more nicely than scope DSL. + def self.visible(*args) + if self == ::Query + # Visibility depends on permissions for each subclass, + # raise an error if the scope is called from Query (eg. Query.visible) + raise Exception.new("Cannot call .visible scope from the base Query class, but from subclasses only.") + end + + user = args.shift || User.current + base = Project.allowed_to_condition(user, view_permission, *args) + scope = joins("LEFT OUTER JOIN #{Project.table_name} ON #{table_name}.project_id = #{Project.table_name}.id"). + where("#{table_name}.project_id IS NULL OR (#{base})") + + if user.admin? + scope.where("#{table_name}.visibility <> ? OR #{table_name}.user_id = ?", VISIBILITY_PRIVATE, user.id) + elsif user.memberships.any? + scope.where("#{table_name}.visibility = ?" + + " OR (#{table_name}.visibility = ? AND #{table_name}.id IN (" + + "SELECT DISTINCT q.id FROM #{table_name} q" + + " 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 #{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))" + + " OR #{table_name}.user_id = ?", + VISIBILITY_PUBLIC, VISIBILITY_ROLES, user.id, Project::STATUS_ARCHIVED, user.id) + elsif user.logged? + scope.where("#{table_name}.visibility = ? OR #{table_name}.user_id = ?", VISIBILITY_PUBLIC, user.id) + else + scope.where("#{table_name}.visibility = ?", VISIBILITY_PUBLIC) + end + end + + # Returns true if the query is visible to +user+ or the current user. + def visible?(user=User.current) + return true if user.admin? + return false unless project.nil? || user.allowed_to?(self.class.view_permission, project) + case visibility + when VISIBILITY_PUBLIC + true + when VISIBILITY_ROLES + if project + (user.roles_for_project(project) & roles).any? + else + user.memberships.joins(:member_roles).where(:member_roles => {:role_id => roles.map(&:id)}).any? + end + else + user == self.user + end + end + + def is_private? + visibility == VISIBILITY_PRIVATE + end + + def is_public? + !is_private? + end + def queried_table_name @queried_table_name ||= self.class.queried_class.table_name end @@ -251,6 +378,7 @@ class Query < ActiveRecord::Base self.group_by = params[:group_by] || (params[:query] && params[:query][:group_by]) self.column_names = params[:c] || (params[:query] && params[:query][:column_names]) self.totalable_names = params[:t] || (params[:query] && params[:query][:totalable_names]) + self.sort_criteria = params[:sort] || (params[:query] && params[:query][:sort_criteria]) self end @@ -259,6 +387,26 @@ class Query < ActiveRecord::Base new(attributes).build_from_params(params) end + def as_params + if new_record? + params = {} + filters.each do |field, options| + params[:f] ||= [] + params[:f] << field + params[:op] ||= {} + params[:op][field] = options[:operator] + params[:v] ||= {} + params[:v][field] = options[:values] + end + params[:c] = column_names + params[:sort] = sort_criteria.to_param + params[:set_filter] = 1 + params + else + {:query_id => id} + end + end + def validate_query_filters filters.each_key do |field| if values_for(field) @@ -312,12 +460,17 @@ class Query < ActiveRecord::Base # Returns a representation of the available filters for JSON serialization def available_filters_as_json json = {} - available_filters.each do |field, options| - options = options.slice(:type, :name, :values) - if options[:values] && values_for(field) - missing = Array(values_for(field)).select(&:present?) - options[:values].map(&:last) - if missing.any? && respond_to?(method = "find_#{field}_filter_values") - options[:values] += send(method, missing) + available_filters.each do |field, filter| + options = {:type => filter[:type], :name => filter[:name]} + options[:remote] = true if filter.remote + + if has_filter?(field) || !filter.remote + options[:values] = filter.values + if options[:values] && values_for(field) + missing = Array(values_for(field)).select(&:present?) - options[:values].map(&:last) + if missing.any? && respond_to?(method = "find_#{field}_filter_values") + options[:values] += send(method, missing) + end end end json[field] = options.stringify_keys @@ -340,6 +493,75 @@ class Query < ActiveRecord::Base @all_projects_values = values end + def project_values + project_values = [] + if User.current.logged? && User.current.memberships.any? + project_values << ["<< #{l(:label_my_projects).downcase} >>", "mine"] + end + project_values += all_projects_values + project_values + end + + def subproject_values + project.descendants.visible.collect{|s| [s.name, s.id.to_s] } + end + + def principals + @principal ||= begin + principals = [] + if project + principals += project.principals.visible + unless project.leaf? + principals += Principal.member_of(project.descendants.visible).visible + end + else + principals += Principal.member_of(all_projects).visible + end + principals.uniq! + principals.sort! + principals.reject! {|p| p.is_a?(GroupBuiltin)} + principals + end + end + + def users + principals.select {|p| p.is_a?(User)} + end + + def author_values + author_values = [] + author_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged? + author_values += users.collect{|s| [s.name, s.id.to_s] } + author_values + end + + def assigned_to_values + assigned_to_values = [] + assigned_to_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged? + assigned_to_values += (Setting.issue_group_assignment? ? principals : users).collect{|s| [s.name, s.id.to_s] } + assigned_to_values + end + + def fixed_version_values + versions = [] + if project + versions = project.shared_versions.to_a + else + versions = Version.visible.where(:sharing => 'system').to_a + end + Version.sort_by_status(versions).collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s, l("version_status_#{s.status}")] } + end + + # Returns a scope of issue statuses that are available as columns for filters + def issue_statuses_values + if project + statuses = project.rolled_up_statuses + else + statuses = IssueStatus.all.sorted + end + statuses.collect{|s| [s.name, s.id.to_s]} + end + # Returns a scope of issue custom fields that are available as columns or filters def issue_custom_fields if project @@ -358,7 +580,7 @@ class Query < ActiveRecord::Base # Adds an available filter def add_available_filter(field, options) @available_filters ||= ActiveSupport::OrderedHash.new - @available_filters[field] = options + @available_filters[field] = QueryFilter.new(field, options) @available_filters end @@ -373,9 +595,7 @@ class Query < ActiveRecord::Base def available_filters unless @available_filters initialize_available_filters - @available_filters.each do |field, options| - options[:name] ||= l(options[:label] || "field_#{field}".gsub(/_id$/, '')) - end + @available_filters ||= {} end @available_filters end @@ -397,7 +617,7 @@ class Query < ActiveRecord::Base next unless expression =~ /^#{Regexp.escape(operator)}(.*)$/ values = $1 add_filter field, operator, values.present? ? values.split('|') : [''] - end || add_filter(field, '=', expression.split('|')) + end || add_filter(field, '=', expression.to_s.split('|')) end # Add multiple filters using +add_filter+ @@ -483,10 +703,17 @@ class Query < ActiveRecord::Base [] end + def default_totalable_names + [] + end + def column_names=(names) if names names = names.select {|n| n.is_a?(Symbol) || !n.blank? } names = names.collect {|n| n.is_a?(Symbol) ? n : n.to_sym } + if names.delete(:all_inline) + names = available_inline_columns.map(&:name) | names + end # Set column_names to nil if default columns if names == default_columns_names names = nil @@ -496,7 +723,8 @@ class Query < ActiveRecord::Base end def has_column?(column) - column_names && column_names.include?(column.is_a?(QueryColumn) ? column.name : column) + name = column.is_a?(QueryColumn) ? column.name : column + columns.detect {|c| c.name == name} end def has_custom_field_column? @@ -520,40 +748,43 @@ class Query < ActiveRecord::Base end def totalable_names - options[:totalable_names] || Setting.issue_list_default_totals.map(&:to_sym) || [] + options[:totalable_names] || default_totalable_names || [] + end + + def default_sort_criteria + [] end def sort_criteria=(arg) - c = [] - if arg.is_a?(Hash) - arg = arg.keys.sort.collect {|k| arg[k]} - end - if arg - c = arg.select {|k,o| !k.to_s.blank?}.slice(0,3).collect {|k,o| [k.to_s, (o == 'desc' || o == false) ? 'desc' : 'asc']} - end - write_attribute(:sort_criteria, c) + c = Redmine::SortCriteria.new(arg) + write_attribute(:sort_criteria, c.to_a) + c end def sort_criteria - read_attribute(:sort_criteria) || [] + c = read_attribute(:sort_criteria) + if c.blank? + c = default_sort_criteria + end + Redmine::SortCriteria.new(c) end - def sort_criteria_key(arg) - sort_criteria && sort_criteria[arg] && sort_criteria[arg].first + def sort_criteria_key(index) + sort_criteria[index].try(:first) end - def sort_criteria_order(arg) - sort_criteria && sort_criteria[arg] && sort_criteria[arg].last + def sort_criteria_order(index) + sort_criteria[index].try(:last) end - def sort_criteria_order_for(key) - sort_criteria.detect {|k, order| key.to_s == k}.try(:last) + def sort_clause + sort_criteria.sort_clause(sortable_columns) end # Returns the SQL sort order that should be prepended for grouping def group_by_sort_order if column = group_by_column - order = (sort_criteria_order_for(column.name) || column.default_order || 'asc').try(:upcase) + order = (sort_criteria.order_for(column.name) || column.default_order || 'asc').try(:upcase) Array(column.sortable).map {|s| "#{s} #{order}"} end end @@ -573,12 +804,19 @@ class Query < ActiveRecord::Base def project_statement project_clauses = [] - if project && !project.descendants.active.empty? + active_subprojects_ids = [] + + active_subprojects_ids = project.descendants.active.map(&:id) if project + if active_subprojects_ids.any? if has_filter?("subproject_id") case operator_for("subproject_id") when '=' # include the selected subprojects - ids = [project.id] + values_for("subproject_id").each(&:to_i) + ids = [project.id] + values_for("subproject_id").map(&:to_i) + project_clauses << "#{Project.table_name}.id IN (%s)" % ids.join(',') + when '!' + # exclude the selected subprojects + ids = [project.id] + active_subprojects_ids - values_for("subproject_id").map(&:to_i) project_clauses << "#{Project.table_name}.id IN (%s)" % ids.join(',') when '!*' # main project only @@ -608,7 +846,7 @@ class Query < ActiveRecord::Base operator = operator_for(field) # "me" value substitution - if %w(assigned_to_id author_id user_id watcher_id).include?(field) + if %w(assigned_to_id author_id user_id watcher_id updated_by last_updated_by).include?(field) if v.delete("me") if User.current.logged? v.push(User.current.id.to_s) @@ -625,12 +863,16 @@ class Query < ActiveRecord::Base end end - if field =~ /cf_(\d+)$/ + if field =~ /^cf_(\d+)\.cf_(\d+)$/ + filters_clauses << sql_for_chained_custom_field(field, operator, v, $1, $2) + elsif field =~ /cf_(\d+)$/ # custom field filters_clauses << sql_for_custom_field(field, operator, v, $1) - elsif respond_to?("sql_for_#{field}_field") + elsif field =~ /^cf_(\d+)\.(.+)$/ + filters_clauses << sql_for_custom_field_attribute(field, operator, v, $1, $2) + elsif respond_to?(method = "sql_for_#{field.gsub('.','_')}_field") # specific statement - filters_clauses << send("sql_for_#{field}_field", field, operator, v) + filters_clauses << send(method, field, operator, v) else # regular field filters_clauses << '(' + sql_for_field(field, operator, v, queried_table_name, field) + ')' @@ -648,6 +890,13 @@ class Query < ActiveRecord::Base filters_clauses.any? ? filters_clauses.join(' AND ') : nil end + # Returns the result count by group or nil if query is not grouped + def result_count_by_group + grouped_query do |scope| + scope.count + end + end + # Returns the sum of values for the given column def total_for(column) total_with_scope(column, base_scope) @@ -673,6 +922,14 @@ class Query < ActiveRecord::Base totals end + def css_classes + s = sort_criteria.first + if s.present? + key, asc = s + "sort-by-#{key.to_s.dasherize} sort-#{asc}" + end + end + private def grouped_query(&block) @@ -768,6 +1025,46 @@ class Query < ActiveRecord::Base " WHERE (#{where}) AND (#{filter[:field].visibility_by_project_condition}))" end + def sql_for_chained_custom_field(field, operator, value, custom_field_id, chained_custom_field_id) + not_in = nil + if operator == '!' + # Makes ! operator work for custom fields with multiple values + operator = '=' + not_in = 'NOT' + end + + filter = available_filters[field] + target_class = filter[:through].format.target_class + + "#{queried_table_name}.id #{not_in} IN (" + + "SELECT customized_id FROM #{CustomValue.table_name}" + + " WHERE customized_type='#{queried_class}' AND custom_field_id=#{custom_field_id}" + + " AND CAST(CASE value WHEN '' THEN '0' ELSE value END AS decimal(30,0)) IN (" + + " SELECT customized_id FROM #{CustomValue.table_name}" + + " WHERE customized_type='#{target_class}' AND custom_field_id=#{chained_custom_field_id}" + + " AND #{sql_for_field(field, operator, value, CustomValue.table_name, 'value')}))" + + end + + def sql_for_custom_field_attribute(field, operator, value, custom_field_id, attribute) + attribute = 'effective_date' if attribute == 'due_date' + not_in = nil + if operator == '!' + # Makes ! operator work for custom fields with multiple values + operator = '=' + not_in = 'NOT' + end + + filter = available_filters[field] + target_table_name = filter[:field].format.target_class.table_name + + "#{queried_table_name}.id #{not_in} IN (" + + "SELECT customized_id FROM #{CustomValue.table_name}" + + " WHERE customized_type='#{queried_class}' AND custom_field_id=#{custom_field_id}" + + " AND CAST(CASE value WHEN '' THEN '0' ELSE value END AS decimal(30,0)) IN (" + + " SELECT id FROM #{target_table_name} WHERE #{sql_for_field(field, operator, value, filter[:field].format.target_class.table_name, attribute)}))" + end + # Helper method to generate the WHERE sql for a +field+, +operator+ and a +value+ def sql_for_field(field, operator, value, db_table, db_field, is_custom_filter=false) sql = '' @@ -810,7 +1107,7 @@ class Query < ActiveRecord::Base end when "!*" sql = "#{db_table}.#{db_field} IS NULL" - sql << " OR #{db_table}.#{db_field} = ''" if is_custom_filter + sql << " OR #{db_table}.#{db_field} = ''" if (is_custom_filter || [:text, :string].include?(type_for(field))) when "*" sql = "#{db_table}.#{db_field} IS NOT NULL" sql << " AND #{db_table}.#{db_field} <> ''" if is_custom_filter @@ -928,11 +1225,6 @@ class Query < ActiveRecord::Base # Adds a filter for the given custom field def add_custom_field_filter(field, assoc=nil) options = field.query_filter_options(self) - if field.format.target_class && field.format.target_class <= User - if options[:values].is_a?(Array) && User.current.logged? - options[:values].unshift ["<< #{l(:label_me)} >>", "me"] - end - end filter_id = "cf_#{field.id}" filter_name = field.name @@ -946,10 +1238,47 @@ class Query < ActiveRecord::Base }) end + # Adds filters for custom fields associated to the custom field target class + # Eg. having a version custom field "Milestone" for issues and a date custom field "Release date" + # for versions, it will add an issue filter on Milestone'e Release date. + def add_chained_custom_field_filters(field) + klass = field.format.target_class + if klass + CustomField.where(:is_filter => true, :type => "#{klass.name}CustomField").each do |chained| + options = chained.query_filter_options(self) + + filter_id = "cf_#{field.id}.cf_#{chained.id}" + filter_name = chained.name + + add_available_filter filter_id, options.merge({ + :name => l(:label_attribute_of_object, :name => chained.name, :object_name => field.name), + :field => chained, + :through => field + }) + end + end + end + # Adds filters for the given custom fields scope def add_custom_fields_filters(scope, assoc=nil) scope.visible.where(:is_filter => true).sorted.each do |field| add_custom_field_filter(field, assoc) + if assoc.nil? + add_chained_custom_field_filters(field) + + if field.format.target_class && field.format.target_class == Version + add_available_filter "cf_#{field.id}.due_date", + :type => :date, + :field => field, + :name => l(:label_attribute_of_object, :name => l(:field_effective_date), :object_name => field.name) + + add_available_filter "cf_#{field.id}.status", + :type => :list, + :field => field, + :name => l(:label_attribute_of_object, :name => l(:field_status), :object_name => field.name), + :values => Version::VERSION_STATUSES.map{|s| [l("version_status_#{s}"), s] } + end + end end end @@ -1031,9 +1360,6 @@ class Query < ActiveRecord::Base joins = [] if order_options - if order_options.include?('authors') - joins << "LEFT OUTER JOIN #{User.table_name} authors ON authors.id = #{queried_table_name}.author_id" - end order_options.scan(/cf_\d+/).uniq.each do |name| column = available_columns.detect {|c| c.name.to_s == name} join = column && column.custom_field.join_for_order_statement diff --git a/app/models/repository.rb b/app/models/repository.rb index 90c2e49ac..fd3a9fbf6 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -285,7 +285,7 @@ class Repository < ActiveRecord::Base # Returns an array of committers usernames and associated user_id def committers - @committers ||= Changeset.where(:repository_id => id).uniq.pluck(:committer, :user_id) + @committers ||= Changeset.where(:repository_id => id).distinct.pluck(:committer, :user_id) end # Maps committers username to a user ids diff --git a/app/models/repository/bazaar.rb b/app/models/repository/bazaar.rb index 6d3cf5bf2..9cc84daa0 100644 --- a/app/models/repository/bazaar.rb +++ b/app/models/repository/bazaar.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/repository/cvs.rb b/app/models/repository/cvs.rb index 565c3b3f8..4d4a51cfd 100644 --- a/app/models/repository/cvs.rb +++ b/app/models/repository/cvs.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -151,7 +151,7 @@ class Repository::Cvs < Repository # create a new changeset.... unless cs # we use a temporary revision number here (just for inserting) - # later on, we calculate a continous positive number + # later on, we calculate a continuous positive number tmp_time2 = tmp_time.clone.gmtime branch = revision.paths[0][:branch] scmid = branch + "-" + tmp_time2.strftime("%Y%m%d-%H%M%S") @@ -163,7 +163,7 @@ class Repository::Cvs < Repository :comments => revision.message) tmp_rev_num += 1 end - # convert CVS-File-States to internal Action-abbrevations + # convert CVS-File-States to internal Action-abbreviations # default action is (M)odified action = "M" if revision.paths[0][:action] == "Exp" && revision.paths[0][:revision] == "1.1" diff --git a/app/models/repository/darcs.rb b/app/models/repository/darcs.rb index a8b613788..8c1302ca2 100644 --- a/app/models/repository/darcs.rb +++ b/app/models/repository/darcs.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/repository/filesystem.rb b/app/models/repository/filesystem.rb index a0b049de8..0a612ae8e 100644 --- a/app/models/repository/filesystem.rb +++ b/app/models/repository/filesystem.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # FileSystem adapter # File written by Paul Rivier, at Demotera. diff --git a/app/models/repository/git.rb b/app/models/repository/git.rb index 0873904d0..893dc533d 100644 --- a/app/models/repository/git.rb +++ b/app/models/repository/git.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # Copyright (C) 2007 Patrick Aljord patcito@ŋmail.com # # This program is free software; you can redistribute it and/or @@ -22,6 +22,8 @@ class Repository::Git < Repository attr_protected :root_url validates_presence_of :url + safe_attributes 'report_last_commit' + def self.human_attribute_name(attribute_key_name, *args) attr_name = attribute_key_name.to_s if attr_name == "url" @@ -39,15 +41,15 @@ class Repository::Git < Repository end def report_last_commit - extra_report_last_commit - end - - def extra_report_last_commit return false if extra_info.nil? v = extra_info["extra_report_last_commit"] return false if v.nil? v.to_s != '0' end + + def report_last_commit=(arg) + merge_extra_info "extra_report_last_commit" => arg + end def supports_directory_revisions? true @@ -94,7 +96,7 @@ class Repository::Git < Repository end def scm_entries(path=nil, identifier=nil) - scm.entries(path, identifier, :report_last_commit => extra_report_last_commit) + scm.entries(path, identifier, :report_last_commit => report_last_commit) end protected :scm_entries @@ -141,7 +143,7 @@ class Repository::Git < Repository return if prev_db_heads.sort == repo_heads.sort h["db_consistent"] ||= {} - if changesets.count == 0 + if ! changesets.exists? h["db_consistent"]["ordering"] = 1 merge_extra_info(h) self.save diff --git a/app/models/repository/mercurial.rb b/app/models/repository/mercurial.rb index 4ab82c58c..b9a767fec 100644 --- a/app/models/repository/mercurial.rb +++ b/app/models/repository/mercurial.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/repository/subversion.rb b/app/models/repository/subversion.rb index 893d71a09..70d497771 100644 --- a/app/models/repository/subversion.rb +++ b/app/models/repository/subversion.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/role.rb b/app/models/role.rb index acf71c50e..8bd2e7258 100644 --- a/app/models/role.rb +++ b/app/models/role.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,6 +16,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class Role < ActiveRecord::Base + include Redmine::SafeAttributes + # Custom coder for the permissions attribute that should be an # array of symbols. Rails 3 uses Psych which can be *unbelievably* # slow on some platforms (eg. mingw32). @@ -59,7 +61,8 @@ class Role < ActiveRecord::Base before_destroy :check_deletable has_many :workflow_rules, :dependent => :delete_all do def copy(source_role) - WorkflowRule.copy(nil, source_role, nil, proxy_association.owner) + ActiveSupport::Deprecation.warn "role.workflow_rules.copy is deprecated and will be removed in Redmine 4.0, use role.copy_worflow_rules instead" + proxy_association.owner.copy_workflow_rules(source_role) end end has_and_belongs_to_many :custom_fields, :join_table => "#{table_name_prefix}custom_fields_roles#{table_name_suffix}", :foreign_key => "role_id" @@ -89,12 +92,25 @@ class Role < ActiveRecord::Base :in => TIME_ENTRIES_VISIBILITY_OPTIONS.collect(&:first), :if => lambda {|role| role.respond_to?(:time_entries_visibility) && role.time_entries_visibility_changed?} + safe_attributes 'name', + 'assignable', + 'position', + 'issues_visibility', + 'users_visibility', + 'time_entries_visibility', + 'all_roles_managed', + 'managed_role_ids', + 'permissions', + 'permissions_all_trackers', + 'permissions_tracker_ids' + # Copies attributes from another role, arg can be an id or a Role def copy_from(arg, options={}) return unless arg.present? role = arg.is_a?(Role) ? arg : Role.find_by_id(arg.to_s) self.attributes = role.attributes.dup.except("id", "name", "position", "builtin", "permissions") self.permissions = role.permissions.dup + self.managed_role_ids = role.managed_role_ids.dup self end @@ -204,7 +220,7 @@ class Role < ActiveRecord::Base end # Returns true if tracker_id belongs to the list of - # trackers for which permission is given + # trackers for which permission is given def permissions_tracker_ids?(permission, tracker_id) permissions_tracker_ids(permission).include?(tracker_id) end @@ -246,6 +262,10 @@ class Role < ActiveRecord::Base self end + def copy_workflow_rules(source_role) + WorkflowRule.copy(nil, source_role, nil, self) + end + # Find all the roles that can be given to a project member def self.find_all_givable Role.givable.to_a diff --git a/app/models/setting.rb b/app/models/setting.rb index 42b179cfc..102bc65e1 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -120,9 +120,15 @@ class Setting < ActiveRecord::Base # Updates multiple settings from params and sends a security notification if needed def self.set_all_from_params(settings) - settings = (settings || {}).dup.symbolize_keys + return nil unless settings.is_a?(Hash) + settings = settings.dup.symbolize_keys + + errors = validate_all_from_params(settings) + return errors if errors.present? + changes = [] settings.each do |name, value| + next unless available_settings[name.to_s] previous_value = Setting[name] set_from_params name, value if available_settings[name.to_s]['security_notifications'] && Setting[name] != previous_value @@ -132,7 +138,29 @@ class Setting < ActiveRecord::Base if changes.any? Mailer.security_settings_updated(changes) end - true + nil + end + + def self.validate_all_from_params(settings) + messages = [] + + if settings.key?(:mail_handler_body_delimiters) || settings.key?(:mail_handler_enable_regex_delimiters) + regexp = Setting.mail_handler_enable_regex_delimiters? + if settings.key?(:mail_handler_enable_regex_delimiters) + regexp = settings[:mail_handler_enable_regex_delimiters].to_s != '0' + end + if regexp + settings[:mail_handler_body_delimiters].to_s.split(/[\r\n]+/).each do |delimiter| + begin + Regexp.new(delimiter) + rescue RegexpError => e + messages << [:mail_handler_body_delimiters, "#{l('activerecord.errors.messages.not_a_regexp')} (#{e.message})"] + end + end + end + end + + messages end # Sets a setting value from params diff --git a/app/models/time_entry.rb b/app/models/time_entry.rb index 10b3b4e27..1e0f5bc95 100644 --- a/app/models/time_entry.rb +++ b/app/models/time_entry.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -42,6 +42,8 @@ class TimeEntry < ActiveRecord::Base :scope => joins(:project).preload(:project) validates_presence_of :user_id, :activity_id, :project_id, :hours, :spent_on + validates_presence_of :issue_id, :if => lambda { Setting.timelog_required_fields.include?('issue_id') } + validates_presence_of :comments, :if => lambda { Setting.timelog_required_fields.include?('comments') } validates_numericality_of :hours, :allow_nil => true, :message => :invalid validates_length_of :comments, :maximum => 1024, :allow_nil => true validates :spent_on, :date => true @@ -52,6 +54,9 @@ class TimeEntry < ActiveRecord::Base joins(:project). where(TimeEntry.visible_condition(args.shift || User.current, *args)) } + scope :left_join_issue, lambda { + joins("LEFT OUTER JOIN #{Issue.table_name} ON #{Issue.table_name}.id = #{TimeEntry.table_name}.issue_id") + } scope :on_issue, lambda {|issue| joins(:issue). where("#{Issue.table_name}.root_id = #{issue.root_id} AND #{Issue.table_name}.lft >= #{issue.lft} AND #{Issue.table_name}.rgt <= #{issue.rgt}") diff --git a/app/models/time_entry_activity.rb b/app/models/time_entry_activity.rb index 0d58d0fd9..62fd18b4d 100644 --- a/app/models/time_entry_activity.rb +++ b/app/models/time_entry_activity.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/time_entry_activity_custom_field.rb b/app/models/time_entry_activity_custom_field.rb index 4ea5357b9..93bc682e6 100644 --- a/app/models/time_entry_activity_custom_field.rb +++ b/app/models/time_entry_activity_custom_field.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/time_entry_custom_field.rb b/app/models/time_entry_custom_field.rb index 8a1c0115c..78bfb9d2b 100644 --- a/app/models/time_entry_custom_field.rb +++ b/app/models/time_entry_custom_field.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/time_entry_query.rb b/app/models/time_entry_query.rb index b5f9c0481..4092efce6 100644 --- a/app/models/time_entry_query.rb +++ b/app/models/time_entry_query.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -18,6 +18,7 @@ class TimeEntryQuery < Query self.queried_class = TimeEntry + self.view_permission = :view_time_entries self.available_columns = [ QueryColumn.new(:project, :sortable => "#{Project.table_name}.name", :groupable => true), @@ -26,61 +27,52 @@ class TimeEntryQuery < Query QueryColumn.new(:user, :sortable => lambda {User.fields_for_order_statement}, :groupable => true), QueryColumn.new(:activity, :sortable => "#{TimeEntryActivity.table_name}.position", :groupable => true), QueryColumn.new(:issue, :sortable => "#{Issue.table_name}.id"), + QueryAssociationColumn.new(:issue, :tracker, :caption => :field_tracker, :sortable => "#{Tracker.table_name}.position"), + QueryAssociationColumn.new(:issue, :status, :caption => :field_status, :sortable => "#{IssueStatus.table_name}.position"), QueryColumn.new(:comments), - QueryColumn.new(:hours, :sortable => "#{TimeEntry.table_name}.hours"), + QueryColumn.new(:hours, :sortable => "#{TimeEntry.table_name}.hours", :totalable => true), ] def initialize(attributes=nil, *args) super attributes - self.filters ||= {} - add_filter('spent_on', '*') unless filters.present? + self.filters ||= { 'spent_on' => {:operator => "*", :values => []} } end def initialize_available_filters add_available_filter "spent_on", :type => :date_past - principals = [] - if project - principals += project.principals.visible.sort - unless project.leaf? - subprojects = project.descendants.visible.to_a - if subprojects.any? - add_available_filter "subproject_id", - :type => :list_subprojects, - :values => subprojects.collect{|s| [s.name, s.id.to_s] } - principals += Principal.member_of(subprojects).visible - end - end - else - if all_projects.any? - # members of visible projects - principals += Principal.member_of(all_projects).visible - # project filter - project_values = [] - if User.current.logged? && User.current.memberships.any? - project_values << ["<< #{l(:label_my_projects).downcase} >>", "mine"] - end - project_values += all_projects_values - add_available_filter("project_id", - :type => :list, :values => project_values - ) unless project_values.empty? - end - end - principals.uniq! - principals.sort! - users = principals.select {|p| p.is_a?(User)} + add_available_filter("project_id", + :type => :list, :values => lambda { project_values } + ) if project.nil? + + if project && !project.leaf? + add_available_filter "subproject_id", + :type => :list_subprojects, + :values => lambda { subproject_values } + end + + add_available_filter("issue_id", :type => :tree, :label => :label_issue) + add_available_filter("issue.tracker_id", + :type => :list, + :name => l("label_attribute_of_issue", :name => l(:field_tracker)), + :values => lambda { trackers.map {|t| [t.name, t.id.to_s]} }) + add_available_filter("issue.status_id", + :type => :list, + :name => l("label_attribute_of_issue", :name => l(:field_status)), + :values => lambda { issue_statuses_values }) + add_available_filter("issue.fixed_version_id", + :type => :list, + :name => l("label_attribute_of_issue", :name => l(:field_fixed_version)), + :values => lambda { fixed_version_values }) - users_values = [] - users_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged? - users_values += users.collect{|s| [s.name, s.id.to_s] } add_available_filter("user_id", - :type => :list_optional, :values => users_values - ) unless users_values.empty? + :type => :list_optional, :values => lambda { author_values } + ) activities = (project ? project.activities : TimeEntryActivity.shared) add_available_filter("activity_id", :type => :list, :values => activities.map {|a| [a.name, a.id.to_s]} - ) unless activities.empty? + ) add_available_filter "comments", :type => :text add_available_filter "hours", :type => :float @@ -97,23 +89,91 @@ class TimeEntryQuery < Query @available_columns += TimeEntryCustomField.visible. map {|cf| QueryCustomFieldColumn.new(cf) } @available_columns += issue_custom_fields.visible. - map {|cf| QueryAssociationCustomFieldColumn.new(:issue, cf) } + map {|cf| QueryAssociationCustomFieldColumn.new(:issue, cf, :totalable => false) } + @available_columns += ProjectCustomField.visible. + map {|cf| QueryAssociationCustomFieldColumn.new(:project, cf) } @available_columns end def default_columns_names - @default_columns_names ||= [:project, :spent_on, :user, :activity, :issue, :comments, :hours] + @default_columns_names ||= begin + default_columns = [:spent_on, :user, :activity, :issue, :comments, :hours] + + project.present? ? default_columns : [:project] | default_columns + end + end + + def default_totalable_names + [:hours] + end + + def default_sort_criteria + [['spent_on', 'desc']] + end + + # If a filter against a single issue is set, returns its id, otherwise nil. + def filtered_issue_id + if value_for('issue_id').to_s =~ /\A(\d+)\z/ + $1 + end + end + + def base_scope + TimeEntry.visible. + joins(:project, :user). + includes(:activity). + references(:activity). + left_join_issue. + where(statement) end def results_scope(options={}) - order_option = [group_by_sort_order, options[:order]].flatten.reject(&:blank?) + order_option = [group_by_sort_order, (options[:order] || sort_clause)].flatten.reject(&:blank?) - TimeEntry.visible. - where(statement). + base_scope. order(order_option). - joins(joins_for_order_statement(order_option.join(','))). - includes(:activity). - references(:activity) + joins(joins_for_order_statement(order_option.join(','))) + end + + # Returns sum of all the spent hours + def total_for_hours(scope) + map_total(scope.sum(:hours)) {|t| t.to_f.round(2)} + end + + def sql_for_issue_id_field(field, operator, value) + case operator + when "=" + "#{TimeEntry.table_name}.issue_id = #{value.first.to_i}" + when "~" + issue = Issue.where(:id => value.first.to_i).first + if issue && (issue_ids = issue.self_and_descendants.pluck(:id)).any? + "#{TimeEntry.table_name}.issue_id IN (#{issue_ids.join(',')})" + else + "1=0" + end + when "!*" + "#{TimeEntry.table_name}.issue_id IS NULL" + when "*" + "#{TimeEntry.table_name}.issue_id IS NOT NULL" + end + end + + def sql_for_issue_fixed_version_id_field(field, operator, value) + issue_ids = Issue.where(:fixed_version_id => value.map(&:to_i)).pluck(:id) + case operator + when "=" + if issue_ids.any? + "#{TimeEntry.table_name}.issue_id IN (#{issue_ids.join(',')})" + else + "1=0" + end + when "!" + if issue_ids.any? + "#{TimeEntry.table_name}.issue_id NOT IN (#{issue_ids.join(',')})" + else + "1=1" + end + end end def sql_for_activity_id_field(field, operator, value) @@ -128,6 +188,14 @@ class TimeEntryQuery < Query end end + def sql_for_issue_tracker_id_field(field, operator, value) + sql_for_field("tracker_id", operator, value, Issue.table_name, "tracker_id") + end + + def sql_for_issue_status_id_field(field, operator, value) + sql_for_field("status_id", operator, value, Issue.table_name, "status_id") + end + # Accepts :from/:to params as shortcut filters def build_from_params(params) super @@ -140,4 +208,20 @@ class TimeEntryQuery < Query end self end + + def joins_for_order_statement(order_options) + joins = [super] + + if order_options + if order_options.include?('issue_statuses') + joins << "LEFT OUTER JOIN #{IssueStatus.table_name} ON #{IssueStatus.table_name}.id = #{Issue.table_name}.status_id" + end + if order_options.include?('trackers') + joins << "LEFT OUTER JOIN #{Tracker.table_name} ON #{Tracker.table_name}.id = #{Issue.table_name}.tracker_id" + end + end + + joins.compact! + joins.any? ? joins.join(' ') : nil + end end diff --git a/app/models/token.rb b/app/models/token.rb index a5ca18aa4..ee43865c1 100644 --- a/app/models/token.rb +++ b/app/models/token.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -25,18 +25,69 @@ class Token < ActiveRecord::Base cattr_accessor :validity_time self.validity_time = 1.day + class << self + attr_reader :actions + + def add_action(name, options) + options.assert_valid_keys(:max_instances, :validity_time) + @actions ||= {} + @actions[name.to_s] = options + end + end + + add_action :api, max_instances: 1, validity_time: nil + add_action :autologin, max_instances: 10, validity_time: Proc.new { Setting.autologin.to_i.days } + add_action :feeds, max_instances: 1, validity_time: nil + add_action :recovery, max_instances: 1, validity_time: Proc.new { Token.validity_time } + add_action :register, max_instances: 1, validity_time: Proc.new { Token.validity_time } + add_action :session, max_instances: 10, validity_time: nil + def generate_new_token self.value = Token.generate_token_value end # Return true if token has expired def expired? - return Time.now > self.created_on + self.class.validity_time + validity_time = self.class.invalid_when_created_before(action) + validity_time.present? && created_on < validity_time + end + + def max_instances + Token.actions.has_key?(action) ? Token.actions[action][:max_instances] : 1 + end + + def self.invalid_when_created_before(action = nil) + if Token.actions.has_key?(action) + validity_time = Token.actions[action][:validity_time] + validity_time = validity_time.call(action) if validity_time.respond_to? :call + else + validity_time = self.validity_time + end + + if validity_time + Time.now - validity_time + end end # Delete all expired tokens def self.destroy_expired - Token.where("action NOT IN (?) AND created_on < ?", ['feeds', 'api', 'session'], Time.now - validity_time).delete_all + t = Token.arel_table + + # Unknown actions have default validity_time + condition = t[:action].not_in(self.actions.keys).and(t[:created_on].lt(invalid_when_created_before)) + + self.actions.each do |action, options| + validity_time = invalid_when_created_before(action) + + # Do not delete tokens, which don't become invalid + next if validity_time.nil? + + condition = condition.or( + t[:action].eq(action).and(t[:created_on].lt(validity_time)) + ) + end + + Token.where(condition).delete_all end # Returns the active user who owns the key for the given action @@ -80,8 +131,8 @@ class Token < ActiveRecord::Base def delete_previous_tokens if user scope = Token.where(:user_id => user.id, :action => action) - if action == 'session' - ids = scope.order(:updated_on => :desc).offset(9).ids + if max_instances > 1 + ids = scope.order(:updated_on => :desc).offset(max_instances - 1).ids if ids.any? Token.delete(ids) end diff --git a/app/models/tracker.rb b/app/models/tracker.rb index 5e4a24b51..59263214f 100644 --- a/app/models/tracker.rb +++ b/app/models/tracker.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,11 +16,12 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class Tracker < ActiveRecord::Base + include Redmine::SafeAttributes - CORE_FIELDS_UNDISABLABLE = %w(project_id tracker_id subject description priority_id is_private).freeze + CORE_FIELDS_UNDISABLABLE = %w(project_id tracker_id subject priority_id is_private).freeze # Fields that can be disabled # Other (future) fields should be appended, not inserted! - CORE_FIELDS = %w(assigned_to_id category_id fixed_version_id parent_issue_id start_date due_date estimated_hours done_ratio).freeze + CORE_FIELDS = %w(assigned_to_id category_id fixed_version_id parent_issue_id start_date due_date estimated_hours done_ratio description).freeze CORE_FIELDS_ALL = (CORE_FIELDS_UNDISABLABLE + CORE_FIELDS).freeze before_destroy :check_integrity @@ -28,10 +29,10 @@ class Tracker < ActiveRecord::Base has_many :issues has_many :workflow_rules, :dependent => :delete_all do def copy(source_tracker) - WorkflowRule.copy(source_tracker, nil, proxy_association.owner, nil) + ActiveSupport::Deprecation.warn "tracker.workflow_rules.copy is deprecated and will be removed in Redmine 4.0, use tracker.copy_worflow_rules instead" + proxy_association.owner.copy_workflow_rules(source_tracker) end end - has_and_belongs_to_many :projects has_and_belongs_to_many :custom_fields, :class_name => 'IssueCustomField', :join_table => "#{table_name_prefix}custom_fields_trackers#{table_name_suffix}", :association_foreign_key => 'custom_field_id' acts_as_positioned @@ -66,9 +67,17 @@ class Tracker < ActiveRecord::Base end end end - joins(:projects).where(condition).uniq + joins(:projects).where(condition).distinct } + safe_attributes 'name', + 'default_status_id', + 'is_in_roadmap', + 'core_fields', + 'position', + 'custom_field_ids', + 'project_ids' + def to_s; name end def <=>(tracker) @@ -85,7 +94,7 @@ class Tracker < ActiveRecord::Base if new_record? [] else - @issue_status_ids ||= WorkflowTransition.where(:tracker_id => id).uniq.pluck(:old_status_id, :new_status_id).flatten.uniq + @issue_status_ids ||= WorkflowTransition.where(:tracker_id => id).distinct.pluck(:old_status_id, :new_status_id).flatten.uniq end end @@ -112,6 +121,10 @@ class Tracker < ActiveRecord::Base core_fields end + def copy_workflow_rules(source_tracker) + WorkflowRule.copy(source_tracker, nil, self, nil) + end + # Returns the fields that are disabled for all the given trackers def self.disabled_core_fields(trackers) if trackers.present? diff --git a/app/models/user.rb b/app/models/user.rb index e90b44738..357856609 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -100,7 +100,7 @@ class User < Principal attr_accessor :remote_ip # Prevents unauthorized assignments - attr_protected :login, :admin, :password, :password_confirmation, :hashed_password + attr_protected :password, :password_confirmation, :hashed_password LOGIN_LENGTH_LIMIT = 60 MAIL_LENGTH_LIMIT = 60 @@ -129,6 +129,10 @@ class User < Principal after_save :update_notified_project_ids, :destroy_tokens, :deliver_security_notification after_destroy :deliver_security_notification + scope :admin, lambda {|*args| + admin = args.size > 0 ? !!args.first : true + where(:admin => admin) + } scope :in_group, lambda {|group| group_id = group.is_a?(Group) ? group.id : group.to_i where("#{User.table_name}.id IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id) @@ -141,7 +145,7 @@ class User < Principal scope :having_mail, lambda {|arg| addresses = Array.wrap(arg).map {|a| a.to_s.downcase} if addresses.any? - joins(:email_addresses).where("LOWER(#{EmailAddress.table_name}.address) IN (?)", addresses).uniq + joins(:email_addresses).where("LOWER(#{EmailAddress.table_name}.address) IN (?)", addresses).distinct else none end @@ -162,7 +166,9 @@ class User < Principal alias :base_reload :reload def reload(*args) @name = nil + @roles = nil @projects_by_role = nil + @project_ids_by_role = nil @membership_by_project_id = nil @notified_projects_ids = nil @notified_projects_ids_changed = false @@ -213,7 +219,7 @@ class User < Principal # Returns the user that matches provided login and password, or nil def self.try_to_login(login, password, active_only=true) - login = login.to_s + login = login.to_s.strip password = password.to_s # Make sure no one can sign in with an empty login or password @@ -411,6 +417,20 @@ class User < Principal token.value end + def delete_session_token(value) + Token.where(:user_id => id, :action => 'session', :value => value).delete_all + end + + # Generates a new autologin token and returns its value + def generate_autologin_token + token = Token.create!(:user_id => id, :action => 'autologin') + token.value + end + + def delete_autologin_token(value) + Token.where(:user_id => id, :action => 'autologin', :value => value).delete_all + end + # Returns true if token is a valid session token for the user whose id is user_id def self.verify_session_token(user_id, token) return false if user_id.blank? || token.blank? @@ -545,6 +565,10 @@ class User < Principal @membership_by_project_id[project_id] end + def roles + @roles ||= Role.joins(members: :project).where(["#{Project.table_name}.status <> ?", Project::STATUS_ARCHIVED]).where(Member.arel_table[:user_id].eq(id)).distinct + end + # Returns the user's bult-in role def builtin_role @builtin_role ||= Role.non_member @@ -564,33 +588,55 @@ class User < Principal end # Returns a hash of user's projects grouped by roles + # TODO: No longer used, should be deprecated def projects_by_role return @projects_by_role if @projects_by_role - hash = Hash.new([]) + result = Hash.new([]) + project_ids_by_role.each do |role, ids| + result[role] = Project.where(:id => ids).to_a + end + @projects_by_role = result + end - group_class = anonymous? ? GroupAnonymous : GroupNonMember - members = Member.joins(:project, :principal). - where("#{Project.table_name}.status <> 9"). - where("#{Member.table_name}.user_id = ? OR (#{Project.table_name}.is_public = ? AND #{Principal.table_name}.type = ?)", self.id, true, group_class.name). - preload(:project, :roles). - to_a - - members.reject! {|member| member.user_id != id && project_ids.include?(member.project_id)} - members.each do |member| - if member.project - member.roles.each do |role| - hash[role] = [] unless hash.key?(role) - hash[role] << member.project + # Returns a hash of project ids grouped by roles. + # Includes the projects that the user is a member of and the projects + # that grant custom permissions to the builtin groups. + def project_ids_by_role + # Clear project condition for when called from chained scopes + # eg. project.children.visible(user) + Project.unscoped do + return @project_ids_by_role if @project_ids_by_role + + group_class = anonymous? ? GroupAnonymous : GroupNonMember + group_id = group_class.pluck(:id).first + + members = Member.joins(:project, :member_roles). + where("#{Project.table_name}.status <> 9"). + where("#{Member.table_name}.user_id = ? OR (#{Project.table_name}.is_public = ? AND #{Member.table_name}.user_id = ?)", self.id, true, group_id). + pluck(:user_id, :role_id, :project_id) + + hash = {} + members.each do |user_id, role_id, project_id| + # Ignore the roles of the builtin group if the user is a member of the project + next if user_id != id && project_ids.include?(project_id) + + hash[role_id] ||= [] + hash[role_id] << project_id + end + + result = Hash.new([]) + if hash.present? + roles = Role.where(:id => hash.keys).to_a + hash.each do |role_id, proj_ids| + role = roles.detect {|r| r.id == role_id} + if role + result[role] = proj_ids.uniq + end end end + @project_ids_by_role = result end - - hash.each do |role, projects| - projects.uniq! - end - - @projects_by_role = hash end # Returns the ids of visible projects @@ -654,8 +700,7 @@ class User < Principal return true if admin? # authorize if user has at least one role that has this permission - roles = memberships.collect {|m| m.roles}.flatten.uniq - roles << (self.logged? ? Role.non_member : Role.anonymous) + roles = self.roles.to_a | [builtin_role] roles.any? {|role| role.allowed_to?(action) && (block_given? ? yield(role, self) : true) @@ -684,7 +729,7 @@ class User < Principal # Returns true if the user is allowed to delete the user's own account def own_account_deletable? Setting.unsubscribe? && - (!admin? || User.active.where("admin = ? AND id <> ?", true, id).exists?) + (!admin? || User.active.admin.where("id <> ?", id).exists?) end safe_attributes 'firstname', @@ -697,10 +742,15 @@ class User < Principal 'custom_fields', 'identity_url' + safe_attributes 'login', + :if => lambda {|user, current_user| user.new_record?} + safe_attributes 'status', 'auth_source_id', 'generate_password', 'must_change_passwd', + 'login', + 'admin', :if => lambda {|user, current_user| current_user.admin?} safe_attributes 'group_ids', @@ -821,11 +871,11 @@ class User < Principal Message.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id]) News.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id]) # Remove private queries and keep public ones - ::Query.delete_all ['user_id = ? AND visibility = ?', id, ::Query::VISIBILITY_PRIVATE] + ::Query.where('user_id = ? AND visibility = ?', id, ::Query::VISIBILITY_PRIVATE).delete_all ::Query.where(['user_id = ?', id]).update_all(['user_id = ?', substitute.id]) TimeEntry.where(['user_id = ?', id]).update_all(['user_id = ?', substitute.id]) - Token.delete_all ['user_id = ?', id] - Watcher.delete_all ['user_id = ?', id] + Token.where('user_id = ?', id).delete_all + Watcher.where('user_id = ?', id).delete_all WikiContent.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id]) WikiContent::Version.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id]) end diff --git a/app/models/user_custom_field.rb b/app/models/user_custom_field.rb index db61b53cd..8b4b34705 100644 --- a/app/models/user_custom_field.rb +++ b/app/models/user_custom_field.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/user_preference.rb b/app/models/user_preference.rb index 0fdbfb508..c6b7fc2ac 100644 --- a/app/models/user_preference.rb +++ b/app/models/user_preference.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,20 +16,36 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class UserPreference < ActiveRecord::Base + include Redmine::SafeAttributes + belongs_to :user serialize :others attr_protected :others, :user_id - before_save :set_others_hash + before_save :set_others_hash, :clear_unused_block_settings + + safe_attributes 'hide_mail', + 'time_zone', + 'comments_sorting', + 'warn_on_leaving_unsaved', + 'no_self_notified', + 'textarea_font' + + TEXTAREA_FONT_OPTIONS = ['monospace', 'proportional'] def initialize(attributes=nil, *args) super - if new_record? && !(attributes && attributes.key?(:hide_mail)) - self.hide_mail = Setting.default_users_hide_mail? - end - if new_record? && !(attributes && attributes.key?(:no_self_notified)) - self.no_self_notified = true + if new_record? + unless attributes && attributes.key?(:hide_mail) + self.hide_mail = Setting.default_users_hide_mail? + end + unless attributes && attributes.key?(:time_zone) + self.time_zone = Setting.default_users_time_zone + end + unless attributes && attributes.key?(:no_self_notified) + self.no_self_notified = true + end end self.others ||= {} end @@ -68,4 +84,85 @@ class UserPreference < ActiveRecord::Base def activity_scope; Array(self[:activity_scope]) ; end def activity_scope=(value); self[:activity_scope]=value ; end + + def textarea_font; self[:textarea_font] end + def textarea_font=(value); self[:textarea_font]=value; end + + # Returns the names of groups that are displayed on user's page + # Example: + # preferences.my_page_groups + # # => ['top', 'left, 'right'] + def my_page_groups + Redmine::MyPage.groups + end + + def my_page_layout + self[:my_page_layout] ||= Redmine::MyPage.default_layout.deep_dup + end + + def my_page_layout=(arg) + self[:my_page_layout] = arg + end + + def my_page_settings(block=nil) + s = self[:my_page_settings] ||= {} + if block + s[block] ||= {} + else + s + end + end + + def my_page_settings=(arg) + self[:my_page_settings] = arg + end + + # Removes block from the user page layout + # Example: + # preferences.remove_block('news') + def remove_block(block) + block = block.to_s.underscore + my_page_layout.keys.each do |group| + my_page_layout[group].delete(block) + end + my_page_layout + end + + # Adds block to the user page layout + # Returns nil if block is not valid or if it's already + # present in the user page layout + def add_block(block) + block = block.to_s.underscore + return unless Redmine::MyPage.valid_block?(block, my_page_layout.values.flatten) + + remove_block(block) + # add it to the first group + group = my_page_groups.first + my_page_layout[group] ||= [] + my_page_layout[group].unshift(block) + end + + # Sets the block order for the given group. + # Example: + # preferences.order_blocks('left', ['issueswatched', 'news']) + def order_blocks(group, blocks) + group = group.to_s + if Redmine::MyPage.groups.include?(group) && blocks.present? + blocks = blocks.map(&:underscore) & my_page_layout.values.flatten + blocks.each {|block| remove_block(block)} + my_page_layout[group] = blocks + end + end + + def update_block_settings(block, settings) + block = block.to_s + block_settings = my_page_settings(block).merge(settings.symbolize_keys) + my_page_settings[block] = block_settings + end + + def clear_unused_block_settings + blocks = my_page_layout.values.flatten + my_page_settings.keep_if {|block, settings| blocks.include?(block)} + end + private :clear_unused_block_settings end diff --git a/app/models/version.rb b/app/models/version.rb index 5c633d46b..a213359f6 100644 --- a/app/models/version.rb +++ b/app/models/version.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -19,6 +19,7 @@ class Version < ActiveRecord::Base include Redmine::SafeAttributes after_update :update_issues_from_sharing_change + after_save :update_default_project_version before_destroy :nullify_projects_default_version belongs_to :project @@ -41,7 +42,18 @@ class Version < ActiveRecord::Base attr_protected :id scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)} + scope :like, lambda {|arg| + if arg.present? + pattern = "%#{arg.to_s.strip}%" + where([Redmine::Database.like("#{Version.table_name}.name", '?'), pattern]) + end + } scope :open, lambda { where(:status => 'open') } + scope :status, lambda {|status| + if status.present? + where(:status => status.to_s) + end + } scope :visible, lambda {|*args| joins(:project). where(Project.allowed_to_condition(args.first || User.current, :view_issues)) @@ -54,6 +66,7 @@ class Version < ActiveRecord::Base 'wiki_page_title', 'status', 'sharing', + 'default_project_version', 'custom_field_values', 'custom_fields' @@ -71,6 +84,12 @@ class Version < ActiveRecord::Base project.present? && project.attachments_deletable?(usr) end + alias :base_reload :reload + def reload(*args) + @default_project_version = nil + base_reload(*args) + end + def start_date @start_date ||= fixed_issues.minimum('start_date') end @@ -197,6 +216,17 @@ class Version < ActiveRecord::Base end end + # Sort versions by status (open, locked then closed versions) + def self.sort_by_status(versions) + versions.sort do |a, b| + if a.status == b.status + a <=> b + else + b.status <=> a.status + end + end + end + def css_classes [ completed? ? 'version-completed' : 'version-incompleted', @@ -241,6 +271,18 @@ class Version < ActiveRecord::Base fixed_issues.empty? && !referenced_by_a_custom_field? end + def default_project_version + if @default_project_version.nil? + project.present? && project.default_version == self + else + @default_project_version + end + end + + def default_project_version=(arg) + @default_project_version = (arg == '1' || arg == true) + end + private def load_issue_counts @@ -269,6 +311,12 @@ class Version < ActiveRecord::Base end end + def update_default_project_version + if @default_project_version && project.present? + project.update_columns :default_version_id => id + end + end + # Returns the average estimated time of assigned issues # or 1 if no issue has an estimated time # Used to weight unestimated issues in progress calculation diff --git a/app/models/version_custom_field.rb b/app/models/version_custom_field.rb index b6b1644b1..f353d7fac 100644 --- a/app/models/version_custom_field.rb +++ b/app/models/version_custom_field.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/watcher.rb b/app/models/watcher.rb index 9dde433eb..6198ceffc 100644 --- a/app/models/watcher.rb +++ b/app/models/watcher.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/wiki.rb b/app/models/wiki.rb index b4cf76595..9ed9554cb 100644 --- a/app/models/wiki.rb +++ b/app/models/wiki.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/wiki_content.rb b/app/models/wiki_content.rb index 3c29f5be7..3d992f8f7 100644 --- a/app/models/wiki_content.rb +++ b/app/models/wiki_content.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 9922fa4be..d7b09f357 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/wiki_redirect.rb b/app/models/wiki_redirect.rb index 76c8447d3..eb4de869a 100644 --- a/app/models/wiki_redirect.rb +++ b/app/models/wiki_redirect.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/models/workflow_permission.rb b/app/models/workflow_permission.rb index 1990b826b..a231978b6 100644 --- a/app/models/workflow_permission.rb +++ b/app/models/workflow_permission.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -46,7 +46,7 @@ class WorkflowPermission < WorkflowRule transaction do permissions.each { |status_id, rule_by_field| rule_by_field.each { |field, rule| - destroy_all(:tracker_id => trackers.map(&:id), :role_id => roles.map(&:id), :old_status_id => status_id, :field_name => field) + where(:tracker_id => trackers.map(&:id), :role_id => roles.map(&:id), :old_status_id => status_id, :field_name => field).destroy_all if rule.present? trackers.each do |tracker| roles.each do |role| diff --git a/app/models/workflow_rule.rb b/app/models/workflow_rule.rb index 6fd4b05f0..8872d84c6 100644 --- a/app/models/workflow_rule.rb +++ b/app/models/workflow_rule.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -29,7 +29,7 @@ class WorkflowRule < ActiveRecord::Base # Copies workflows from source to targets def self.copy(source_tracker, source_role, target_trackers, target_roles) unless source_tracker.is_a?(Tracker) || source_role.is_a?(Role) - raise ArgumentError.new("source_tracker or source_role must be specified") + raise ArgumentError.new("source_tracker or source_role must be specified, given: #{source_tracker.class.name} and #{source_role.class.name}") end target_trackers = [target_trackers].flatten.compact @@ -62,7 +62,7 @@ class WorkflowRule < ActiveRecord::Base false else transaction do - delete_all :tracker_id => target_tracker.id, :role_id => target_role.id + where(:tracker_id => target_tracker.id, :role_id => target_role.id).delete_all connection.insert "INSERT INTO #{WorkflowRule.table_name} (tracker_id, role_id, old_status_id, new_status_id, author, assignee, field_name, #{connection.quote_column_name 'rule'}, type)" + " SELECT #{target_tracker.id}, #{target_role.id}, old_status_id, new_status_id, author, assignee, field_name, #{connection.quote_column_name 'rule'}, type" + " FROM #{WorkflowRule.table_name}" + diff --git a/app/models/workflow_transition.rb b/app/models/workflow_transition.rb index ff75254f2..4f161df1f 100644 --- a/app/models/workflow_transition.rb +++ b/app/models/workflow_transition.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/views/account/login.html.erb b/app/views/account/login.html.erb index f1b96bae3..1440e3227 100644 --- a/app/views/account/login.html.erb +++ b/app/views/account/login.html.erb @@ -1,43 +1,31 @@ <%= call_hook :view_account_login_top %> +
      -<%= form_tag(signin_path, onsubmit: 'return keepAnchorOnSignIn(this);') do %> -<%= back_url_hidden_field_tag %> - - - - - - - - - -<% if Setting.openid? %> - - - - -<% end %> - - - - - - - - -
      <%= text_field_tag 'username', params[:username], :tabindex => '1' %>
      <%= password_field_tag 'password', nil, :tabindex => '2' %>
      <%= text_field_tag "openid_url", nil, :tabindex => '3' %>
      - <% if Setting.autologin? %> - - <% end %> -
      - <% if Setting.lost_password? %> - <%= link_to l(:label_password_lost), lost_password_path %> - <% end %> - - -
      -<% end %> + <%= form_tag(signin_path, onsubmit: 'return keepAnchorOnSignIn(this);') do %> + <%= back_url_hidden_field_tag %> + + + <%= text_field_tag 'username', params[:username], :tabindex => '1' %> + + + <%= password_field_tag 'password', nil, :tabindex => '2' %> + + <% if Setting.openid? %> + + <%= text_field_tag "openid_url", nil, :tabindex => '3' %> + <% end %> + + <% if Setting.autologin? %> + + <% end %> + + + <% end %>
      + <%= call_hook :view_account_login_bottom %> <% if params[:username].present? %> diff --git a/app/views/account/register.html.erb b/app/views/account/register.html.erb index 0cd0d8cff..ade00adfc 100644 --- a/app/views/account/register.html.erb +++ b/app/views/account/register.html.erb @@ -29,7 +29,7 @@

      <%= f.text_field :identity_url %>

      <% end %> -<% @user.custom_field_values.select {|v| v.editable? || v.required?}.each do |value| %> +<% @user.custom_field_values.select {|v| (Setting.show_custom_fields_on_registration? && v.editable?) || v.required?}.each do |value| %>

      <%= custom_field_tag_with_label :user, value %>

      <% end %> diff --git a/app/views/activities/index.html.erb b/app/views/activities/index.html.erb index c191ccba5..c3026fb63 100644 --- a/app/views/activities/index.html.erb +++ b/app/views/activities/index.html.erb @@ -6,7 +6,7 @@

      <%= format_activity_day(day) %>

      <% sort_activity_events(@events_by_day[day]).each do |e, in_group| -%> -
      <%= User.current.logged? && e.respond_to?(:event_author) && User.current == e.event_author ? 'me' : nil %>"> +
      <%= User.current.logged? && e.respond_to?(:event_author) && User.current == e.event_author ? 'me' : nil %>"> <%= avatar(e.event_author, :size => "24") if e.respond_to?(:event_author) %> <%= format_time(e.event_datetime, false) %> <%= content_tag('span', e.project, :class => 'project') if @project.nil? || @project != e.project %> @@ -21,29 +21,32 @@ <%= content_tag('p', l(:label_no_data), :class => 'nodata') if @events_by_day.empty? %> -
      -<%= link_to_content_update("\xc2\xab " + l(:label_previous), - params.merge(:from => @date_to - @days - 1), + +
        +
      -
      -<%= link_to_content_update(l(:label_next) + " \xc2\xbb", - params.merge(:from => @date_to + @days - 1), +
    • <% unless @date_to > User.current.today %> +
    +   <% other_formats_links do |f| %> - <%= f.link_to 'Atom', :url => params.merge(:from => nil, :key => User.current.rss_key) %> + <%= f.link_to_with_query_parameters 'Atom', 'from' => nil, :key => User.current.rss_key %> <% end %> <% content_for :header_tags do %> -<%= auto_discovery_link_tag(:atom, params.merge(:format => 'atom', :from => nil, :key => User.current.rss_key)) %> +<%= auto_discovery_link_tag(:atom, :params => request.query_parameters.merge(:from => nil, :key => User.current.rss_key), :format => 'atom') %> <% end %> <% content_for :sidebar do %> -<%= form_tag({}, :method => :get) do %> +<%= form_tag({}, :method => :get, :id => 'activity_scope_form') do %>

    <%= l(:label_activity) %>

      <% @activity.event_types.each do |t| %> diff --git a/app/views/admin/info.html.erb b/app/views/admin/info.html.erb index 2be1d28fd..c56d14eb6 100644 --- a/app/views/admin/info.html.erb +++ b/app/views/admin/info.html.erb @@ -4,7 +4,7 @@ <% @checklist.each do |label, result| %> - + diff --git a/app/views/admin/plugins.html.erb b/app/views/admin/plugins.html.erb index 020dbc818..e1a1b2a64 100644 --- a/app/views/admin/plugins.html.erb +++ b/app/views/admin/plugins.html.erb @@ -1,19 +1,21 @@ <%= title l(:label_plugins) %> <% if @plugins.any? %> -
      <%= label.is_a?(Symbol) ? l(label) : label %>
      - <% @plugins.each do |plugin| %> - - - - - - - <% end %> -
      <%= plugin.name %> - <%= content_tag('span', plugin.description, :class => 'description') unless plugin.description.blank? %> - <%= content_tag('span', link_to(plugin.url, plugin.url), :class => 'url') unless plugin.url.blank? %> - <%= plugin.author_url.blank? ? plugin.author : link_to(plugin.author, plugin.author_url) %><%= plugin.version %><%= link_to(l(:button_configure), plugin_settings_path(plugin)) if plugin.configurable? %>
      +
      + + <% @plugins.each do |plugin| %> + + + + + + + <% end %> +
      <%= plugin.name %> + <%= content_tag('span', plugin.description, :class => 'description') unless plugin.description.blank? %> + <%= content_tag('span', link_to(plugin.url, plugin.url), :class => 'url') unless plugin.url.blank? %> + <%= plugin.author_url.blank? ? plugin.author : link_to(plugin.author, plugin.author_url) %><%= plugin.version %><%= link_to(l(:button_configure), plugin_settings_path(plugin)) if plugin.configurable? %>
      +

      <%= l(:label_check_for_updates) %>

      <% else %>

      <%= l(:label_no_data) %>

      diff --git a/app/views/admin/projects.html.erb b/app/views/admin/projects.html.erb index 0437f9e28..077f87c27 100644 --- a/app/views/admin/projects.html.erb +++ b/app/views/admin/projects.html.erb @@ -6,7 +6,7 @@ <%= form_tag({}, :method => :get) do %>
      <%= l(:label_filter_plural) %> - + <%= select_tag 'status', project_status_options_for_select(@status), :class => "small", :onchange => "this.form.submit(); return false;" %> <%= text_field_tag 'name', params[:name], :size => 30 %> @@ -16,6 +16,7 @@ <% end %>   +<% if @projects.any? %>
      @@ -25,8 +26,8 @@ -<% project_tree(@projects) do |project, level| %> - <%= project.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>"> +<% project_tree(@projects, :init_level => true) do |project, level| %> + "> @@ -41,3 +42,7 @@
      <%= link_to_project_settings(project, {}, :title => project.short_description) %> <%= checked_image project.is_public? %> <%= format_date(project.created_on) %>
      +<%= pagination_links_full @project_pages, @project_count %> +<% else %> +

      <%= l(:label_no_data) %>

      +<% end %> diff --git a/app/views/attachments/_form.html.erb b/app/views/attachments/_form.html.erb index 65ad8804a..96c8c6476 100644 --- a/app/views/attachments/_form.html.erb +++ b/app/views/attachments/_form.html.erb @@ -1,29 +1,45 @@ - -<% if defined?(container) && container && container.saved_attachments %> - <% container.saved_attachments.each_with_index do |attachment, i| %> - - <%= text_field_tag("attachments[p#{i}][filename]", attachment.filename, :class => 'filename') + - text_field_tag("attachments[p#{i}][description]", attachment.description, :maxlength => 255, :placeholder => l(:label_optional_description), :class => 'description') + - link_to(' '.html_safe, attachment_path(attachment, :attachment_id => "p#{i}", :format => 'js'), :method => 'delete', :remote => true, :class => 'remove-upload') %> - <%= hidden_field_tag "attachments[p#{i}][token]", "#{attachment.token}" %> - +<% attachment_param ||= 'attachments' %> +<% saved_attachments ||= container.saved_attachments if defined?(container) && container %> +<% multiple = true unless defined?(multiple) && multiple == false %> +<% show_add = multiple || saved_attachments.blank? %> +<% description = (defined?(description) && description == false ? false : true) %> +<% css_class = (defined?(filedrop) && filedrop == false ? '' : 'filedrop') %> + + + + <% if saved_attachments.present? %> + <% saved_attachments.each_with_index do |attachment, i| %> + + <%= text_field_tag("#{attachment_param}[p#{i}][filename]", attachment.filename, :class => 'filename') %> + <% if attachment.container_id.present? %> + <%= link_to l(:label_delete), "#", :onclick => "$(this).closest('.attachments_form').find('.add_attachment').show(); $(this).parent().remove(); return false;", :class => 'icon-only icon-del' %> + <%= hidden_field_tag "#{attachment_param}[p#{i}][id]", attachment.id %> + <% else %> + <%= text_field_tag("#{attachment_param}[p#{i}][description]", attachment.description, :maxlength => 255, :placeholder => l(:label_optional_description), :class => 'description') if description %> + <%= link_to(' '.html_safe, attachment_path(attachment, :attachment_id => "p#{i}", :format => 'js'), :method => 'delete', :remote => true, :class => 'icon-only icon-del remove-upload') %> + <%= hidden_field_tag "#{attachment_param}[p#{i}][token]", attachment.token %> + <% end %> + + <% end %> <% end %> -<% end %> - - -<%= file_field_tag 'attachments[dummy][file]', - :id => nil, - :class => 'file_selector', - :multiple => true, - :onchange => 'addInputFiles(this);', - :data => { - :max_file_size => Setting.attachment_max_size.to_i.kilobytes, - :max_file_size_message => l(:error_attachment_too_big, :max_size => number_to_human_size(Setting.attachment_max_size.to_i.kilobytes)), - :max_concurrent_uploads => Redmine::Configuration['max_concurrent_ajax_uploads'].to_i, - :upload_path => uploads_path(:format => 'js'), - :description_placeholder => l(:label_optional_description) - } %> -(<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>) + + + <%= file_field_tag "#{attachment_param}[dummy][file]", + :id => nil, + :class => "file_selector #{css_class}", + :multiple => multiple, + :onchange => 'addInputFiles(this);', + :data => { + :max_file_size => Setting.attachment_max_size.to_i.kilobytes, + :max_file_size_message => l(:error_attachment_too_big, :max_size => number_to_human_size(Setting.attachment_max_size.to_i.kilobytes)), + :max_concurrent_uploads => Redmine::Configuration['max_concurrent_ajax_uploads'].to_i, + :upload_path => uploads_path(:format => 'js'), + :param => attachment_param, + :description => description, + :description_placeholder => l(:label_optional_description) + } %> + (<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>) + <% content_for :header_tags do %> diff --git a/app/views/attachments/_links.html.erb b/app/views/attachments/_links.html.erb index 797f8bb43..0a9f5e3eb 100644 --- a/app/views/attachments/_links.html.erb +++ b/app/views/attachments/_links.html.erb @@ -6,29 +6,32 @@ :class => 'icon-only icon-edit' ) if options[:editable] %> + <% for attachment in attachments %> -

      <%= link_to_attachment attachment, :class => 'icon icon-attachment', :download => true -%> - <% if attachment.is_text? || attachment.is_image? %> - <%= link_to l(:button_view), - { :controller => 'attachments', :action => 'show', - :id => attachment, :filename => attachment.filename }, - :class => 'icon-only icon-magnifier', - :title => l(:button_view) %> - <% end %> - <%= " - #{attachment.description}" unless attachment.description.blank? %> - (<%= number_to_human_size attachment.filesize %>) - <% if options[:deletable] %> - <%= link_to l(:button_delete), attachment_path(attachment), - :data => {:confirm => l(:text_are_you_sure)}, - :method => :delete, - :class => 'delete icon-only icon-del', - :title => l(:button_delete) %> - <% end %> - <% if options[:author] %> - <%= attachment.author %>, <%= format_time(attachment.created_on) %> - <% end %> -

      + + + + + + <% end %> +
      + <%= link_to_attachment attachment, class: 'icon icon-attachment' -%> + (<%= number_to_human_size attachment.filesize %>) + <%= link_to_attachment attachment, class: 'icon-only icon-download', title: l(:button_download), download: true -%> + <%= attachment.description unless attachment.description.blank? %> + <% if options[:author] %> + <%= attachment.author %>, <%= format_time(attachment.created_on) %> + <% end %> + + <% if options[:deletable] %> + <%= link_to l(:button_delete), attachment_path(attachment), + :data => {:confirm => l(:text_are_you_sure)}, + :method => :delete, + :class => 'delete icon-only icon-del', + :title => l(:button_delete) %> + <% end %> +
      <% if defined?(thumbnails) && thumbnails %> <% images = attachments.select(&:thumbnailable?) %> <% if images.any? %> diff --git a/app/views/attachments/destroy.js.erb b/app/views/attachments/destroy.js.erb index 3cfb5845f..29b9a0c76 100644 --- a/app/views/attachments/destroy.js.erb +++ b/app/views/attachments/destroy.js.erb @@ -1 +1,2 @@ +$('#attachments_<%= j params[:attachment_id] %>').closest('.attachments_form').find('.add_attachment').show(); $('#attachments_<%= j params[:attachment_id] %>').remove(); diff --git a/app/views/attachments/edit.html.erb b/app/views/attachments/edit_all.html.erb similarity index 100% rename from app/views/attachments/edit.html.erb rename to app/views/attachments/edit_all.html.erb diff --git a/app/views/attachments/image.html.erb b/app/views/attachments/image.html.erb index 306458366..b0d2258d7 100644 --- a/app/views/attachments/image.html.erb +++ b/app/views/attachments/image.html.erb @@ -1,3 +1,3 @@ <%= render :layout => 'layouts/file' do %> - <%= render :partial => 'common/image', :locals => {:path => download_named_attachment_url(@attachment, @attachment.filename), :alt => @attachment.filename} %> + <%= render :partial => 'common/image', :locals => {:path => download_named_attachment_path(@attachment, @attachment.filename), :alt => @attachment.filename} %> <% end %> diff --git a/app/views/attachments/other.html.erb b/app/views/attachments/other.html.erb index e6244c16e..608bbf232 100644 --- a/app/views/attachments/other.html.erb +++ b/app/views/attachments/other.html.erb @@ -1,3 +1,11 @@ <%= render :layout => 'layouts/file' do %> - <%= render :partial => 'common/other' %> + <%= render :partial => 'common/other', + :locals => { + :download_link => link_to_attachment( + @attachment, + :text => l(:label_no_preview_download), + :download => true, + :class => 'icon icon-download' + ) + } %> <% end %> diff --git a/app/views/attachments/upload.api.rsb b/app/views/attachments/upload.api.rsb index edd0b0af4..6049b2ed7 100644 --- a/app/views/attachments/upload.api.rsb +++ b/app/views/attachments/upload.api.rsb @@ -1,3 +1,4 @@ api.upload do + api.id @attachment.id api.token @attachment.token end diff --git a/app/views/attachments/upload.js.erb b/app/views/attachments/upload.js.erb index acd8f83e1..6b804a62b 100644 --- a/app/views/attachments/upload.js.erb +++ b/app/views/attachments/upload.js.erb @@ -3,7 +3,7 @@ var fileSpan = $('#attachments_<%= j params[:attachment_id] %>'); fileSpan.hide(); alert("<%= escape_javascript @attachment.errors.full_messages.join(', ') %>"); <% else %> -$('', { type: 'hidden', name: 'attachments[<%= j params[:attachment_id] %>][token]' } ).val('<%= j @attachment.token %>').appendTo(fileSpan); +fileSpan.find('input.token').val('<%= j @attachment.token %>'); fileSpan.find('a.remove-upload') .attr({ "data-remote": true, diff --git a/app/views/auth_sources/index.html.erb b/app/views/auth_sources/index.html.erb index ecde2bf95..7a0ffa59d 100644 --- a/app/views/auth_sources/index.html.erb +++ b/app/views/auth_sources/index.html.erb @@ -14,7 +14,7 @@ <% for source in @auth_sources %> - "> + <%= link_to(source.name, :action => 'edit', :id => source)%> <%= source.auth_method_name %> <%= source.host %> diff --git a/app/views/boards/index.html.erb b/app/views/boards/index.html.erb index 29074130b..21280e1af 100644 --- a/app/views/boards/index.html.erb +++ b/app/views/boards/index.html.erb @@ -9,7 +9,7 @@ <% Board.board_tree(@boards) do |board, level| %> - + <%= link_to board.name, project_board_path(board.project, board), :class => "board" %>
      <%=h board.description %> diff --git a/app/views/boards/show.html.erb b/app/views/boards/show.html.erb index a9734318a..4a0a588e9 100644 --- a/app/views/boards/show.html.erb +++ b/app/views/boards/show.html.erb @@ -35,8 +35,8 @@ <% @topics.each do |topic| %> - - <%= link_to topic.subject, board_message_path(@board, topic) %> + + <%= link_to topic.subject, board_message_path(@board, topic) %> <%= link_to_user(topic.author) %> <%= format_time(topic.created_on) %> <%= topic.replies_count %> diff --git a/app/views/common/_other.html.erb b/app/views/common/_other.html.erb index fe0228a50..74d87a60a 100644 --- a/app/views/common/_other.html.erb +++ b/app/views/common/_other.html.erb @@ -1 +1,7 @@ -

      <%= l(:label_no_preview) %>

      +

      + <% if defined? download_link %> + <%= t(:label_no_preview_alternative_html, link: download_link) %> + <% else %> + <%= l(:label_no_preview) %> + <% end %> +

      diff --git a/app/views/common/_tabs.html.erb b/app/views/common/_tabs.html.erb index 705ecc710..1b880c9c7 100644 --- a/app/views/common/_tabs.html.erb +++ b/app/views/common/_tabs.html.erb @@ -1,10 +1,10 @@
        <% tabs.each do |tab| -%> -
      • <%= link_to l(tab[:label]), { :tab => tab[:name] }, +
      • <%= link_to l(tab[:label]), (tab[:url] || { :tab => tab[:name] }), :id => "tab-#{tab[:name]}", :class => (tab[:name] != selected_tab ? nil : 'selected'), - :onclick => "showTab('#{tab[:name]}', this.href); this.blur(); return false;" %>
      • + :onclick => tab[:partial] ? "showTab('#{tab[:name]}', this.href); this.blur(); return false;" : nil %> <% end -%>
  • <% end %> diff --git a/app/views/custom_fields/_form.html.erb b/app/views/custom_fields/_form.html.erb index 7c79189ac..94f417cfa 100644 --- a/app/views/custom_fields/_form.html.erb +++ b/app/views/custom_fields/_form.html.erb @@ -27,37 +27,20 @@ <% case @custom_field.class.name when "IssueCustomField" %>

    <%= f.check_box :is_required %>

    -

    <%= f.check_box :is_for_all, :data => {:disables => '#custom_field_project_ids input'} %>

    + <% if @custom_field.format.is_filter_supported %>

    <%= f.check_box :is_filter %>

    + <% end %> <% if @custom_field.format.searchable_supported %>

    <%= f.check_box :searchable %>

    <% end %> -

    - - - - <% Role.givable.sorted.each do |role| %> - - <% end %> - <%= hidden_field_tag 'custom_field[role_ids][]', '' %> -

    <% when "UserCustomField" %>

    <%= f.check_box :is_required %>

    <%= f.check_box :visible %>

    <%= f.check_box :editable %>

    + <% if @custom_field.format.is_filter_supported %>

    <%= f.check_box :is_filter %>

    + <% end %> <% when "ProjectCustomField" %>

    <%= f.check_box :is_required %>

    @@ -65,19 +48,27 @@ when "IssueCustomField" %> <% if @custom_field.format.searchable_supported %>

    <%= f.check_box :searchable %>

    <% end %> + <% if @custom_field.format.is_filter_supported %>

    <%= f.check_box :is_filter %>

    + <% end %> <% when "VersionCustomField" %>

    <%= f.check_box :is_required %>

    + <% if @custom_field.format.is_filter_supported %>

    <%= f.check_box :is_filter %>

    + <% end %> <% when "GroupCustomField" %>

    <%= f.check_box :is_required %>

    + <% if @custom_field.format.is_filter_supported %>

    <%= f.check_box :is_filter %>

    + <% end %> <% when "TimeEntryCustomField" %>

    <%= f.check_box :is_required %>

    + <% if @custom_field.format.is_filter_supported %>

    <%= f.check_box :is_filter %>

    + <% end %> <% else %>

    <%= f.check_box :is_required %>

    @@ -87,11 +78,34 @@ when "IssueCustomField" %> <% if @custom_field.is_a?(IssueCustomField) %> + +
    <%= l(:field_visible) %> + + + <% role_ids = @custom_field.role_ids %> + <% Role.givable.sorted.each do |role| %> + + <% end %> + <%= hidden_field_tag 'custom_field[role_ids][]', '' %> +
    +
    <%=l(:label_tracker_plural)%> + <% tracker_ids = @custom_field.tracker_ids %> <% Tracker.sorted.each do |tracker| %> <%= check_box_tag "custom_field[tracker_ids][]", tracker.id, - (@custom_field.trackers.include? tracker), + tracker_ids.include?(tracker.id), :id => "custom_field_tracker_ids_#{tracker.id}" %>
    -
    <%= l(:label_project_plural) %> - <% project_ids = @custom_field.project_ids.to_a %> - <%= render_project_nested_lists(Project.all) do |p| - content_tag('label', check_box_tag('custom_field[project_ids][]', p.id, project_ids.include?(p.id), :id => nil) + ' ' + p) - end %> - <%= hidden_field_tag('custom_field[project_ids][]', '', :id => nil) %> -

    <%= check_all_links 'custom_field_project_ids' %>

    +
    <%= l(:label_project_plural) %> +

    <%= f.check_box :is_for_all, :data => {:disables => '#custom_field_project_ids input'} %>

    + +
    + <% project_ids = @custom_field.project_ids.to_a %> + <%= render_project_nested_lists(Project.all) do |p| + content_tag('label', check_box_tag('custom_field[project_ids][]', p.id, project_ids.include?(p.id), :id => nil) + ' ' + p) + end %> + <%= hidden_field_tag('custom_field[project_ids][]', '', :id => nil) %> +

    <%= check_all_links 'custom_field_project_ids' %>

    +
    <% end %> diff --git a/app/views/custom_fields/_index.html.erb b/app/views/custom_fields/_index.html.erb index 7a5d37283..04d4aa21d 100644 --- a/app/views/custom_fields/_index.html.erb +++ b/app/views/custom_fields/_index.html.erb @@ -12,19 +12,19 @@ <% (@custom_fields_by_type[tab[:name]] || []).sort.each do |custom_field| -%> <% back_url = custom_fields_path(:tab => tab[:name]) %> - "> + <%= link_to custom_field.name, edit_custom_field_path(custom_field) %> <%= l(custom_field.format.label) %> <%= checked_image custom_field.is_required? %> <% if tab[:name] == 'IssueCustomField' %> <%= checked_image custom_field.is_for_all? %> - <%= l(:label_x_projects, :count => custom_field.projects.count) if custom_field.is_a? IssueCustomField and !custom_field.is_for_all? %> + <%= l(:label_x_projects, :count => @custom_fields_projects_count[custom_field.id] || 0) if custom_field.is_a? IssueCustomField and !custom_field.is_for_all? %> <% end %> <%= reorder_handle(custom_field, :url => custom_field_path(custom_field), :param => 'custom_field') %> <%= delete_link custom_field_path(custom_field) %> - <% end; reset_cycle %> + <% end %> diff --git a/app/views/custom_fields/formats/_attachment.html.erb b/app/views/custom_fields/formats/_attachment.html.erb new file mode 100644 index 000000000..263238a1b --- /dev/null +++ b/app/views/custom_fields/formats/_attachment.html.erb @@ -0,0 +1,4 @@ +

    + <%= f.text_field :extensions_allowed, :size => 50, :label => :setting_attachment_extensions_allowed %> + <%= l(:text_comma_separated) %> <%= l(:label_example) %>: txt, png +

    diff --git a/app/views/custom_fields/formats/_text.html.erb b/app/views/custom_fields/formats/_text.html.erb index e72dcab42..79ea7c5e6 100644 --- a/app/views/custom_fields/formats/_text.html.erb +++ b/app/views/custom_fields/formats/_text.html.erb @@ -1,3 +1,6 @@ <%= render :partial => 'custom_fields/formats/regexp', :locals => {:f => f, :custom_field => custom_field} %>

    <%= f.check_box :text_formatting, {:label => :setting_text_formatting}, 'full', '' %>

    +<% if @custom_field.class.name == "IssueCustomField" %> +

    <%= f.check_box :full_width_layout %>

    +<% end %>

    <%= f.text_area(:default_value, :rows => 5) %>

    diff --git a/app/views/documents/edit.html.erb b/app/views/documents/edit.html.erb index 15491168d..87e08e136 100644 --- a/app/views/documents/edit.html.erb +++ b/app/views/documents/edit.html.erb @@ -1,6 +1,6 @@

    <%=l(:label_document)%>

    -<%= labelled_form_for @document do |f| %> +<%= labelled_form_for @document, :html => {:multipart => true} do |f| %> <%= render :partial => 'form', :locals => {:f => f} %>

    <%= submit_tag l(:button_save) %>

    <% end %> diff --git a/app/views/documents/show.html.erb b/app/views/documents/show.html.erb index 8d2e495c0..799803c9e 100644 --- a/app/views/documents/show.html.erb +++ b/app/views/documents/show.html.erb @@ -25,7 +25,7 @@

    <%= l(:label_attachment_plural) %>

    -<%= link_to_attachments @document %> +<%= link_to_attachments @document, :thumbnails => true %> <% if authorize_for('documents', 'add_attachment') %>

    <%= link_to l(:label_attachment_new), {}, :onclick => "$('#add_attachment_form').show(); return false;", diff --git a/app/views/email_addresses/_index.html.erb b/app/views/email_addresses/_index.html.erb index 644cd759b..2fcf0b496 100644 --- a/app/views/email_addresses/_index.html.erb +++ b/app/views/email_addresses/_index.html.erb @@ -1,7 +1,7 @@ <% if @addresses.present? %> <% @addresses.each do |address| %> - "> + + @@ -26,19 +26,19 @@ <% end -%> <% container.attachments.each do |file| %> - "> - + + - + - <% end - reset_cycle %> + <% end %> <% end %> diff --git a/app/views/gantts/show.html.erb b/app/views/gantts/show.html.erb index e6ae03ad0..314c2f2c5 100644 --- a/app/views/gantts/show.html.erb +++ b/app/views/gantts/show.html.erb @@ -332,21 +332,21 @@
    - <%= link_to_content_update("\xc2\xab " + l(:label_previous), - params.merge(@gantt.params_previous), + <%= link_to("\xc2\xab " + l(:label_previous), + {:params => request.query_parameters.merge(@gantt.params_previous)}, :accesskey => accesskey(:previous)) %> - <%= link_to_content_update(l(:label_next) + " \xc2\xbb", - params.merge(@gantt.params_next), + <%= link_to(l(:label_next) + " \xc2\xbb", + {:params => request.query_parameters.merge(@gantt.params_next)}, :accesskey => accesskey(:next)) %>
    <% other_formats_links do |f| %> - <%= f.link_to 'PDF', :url => params.merge(@gantt.params) %> - <%= f.link_to('PNG', :url => params.merge(@gantt.params)) if @gantt.respond_to?('to_image') %> + <%= f.link_to_with_query_parameters 'PDF', @gantt.params %> + <%= f.link_to_with_query_parameters('PNG', @gantt.params) if @gantt.respond_to?('to_image') %> <% end %> <% end # query.valid? %> diff --git a/app/views/groups/_general.html.erb b/app/views/groups/_general.html.erb index c48f54cd4..9cc5be6c3 100644 --- a/app/views/groups/_general.html.erb +++ b/app/views/groups/_general.html.erb @@ -1,4 +1,4 @@ -<%= labelled_form_for @group, :url => group_path(@group) do |f| %> +<%= labelled_form_for @group, :url => group_path(@group), :html => {:multipart => true} do |f| %> <%= render :partial => 'form', :locals => { :f => f } %> <%= submit_tag l(:button_save) %> <% end %> diff --git a/app/views/groups/_users.html.erb b/app/views/groups/_users.html.erb index 391edf4d8..8c2ab7e4b 100644 --- a/app/views/groups/_users.html.erb +++ b/app/views/groups/_users.html.erb @@ -8,7 +8,7 @@ <% @group.users.sort.each do |user| %> - + <%= link_to_user user %> <%= delete_link group_user_path(@group, :user_id => user), :remote => true %> diff --git a/app/views/groups/index.html.erb b/app/views/groups/index.html.erb index 888b4085d..7b77fec22 100644 --- a/app/views/groups/index.html.erb +++ b/app/views/groups/index.html.erb @@ -3,7 +3,19 @@ <%= title l(:label_group_plural) %> + +<%= form_tag(groups_path, :method => :get) do %> +

    <%= l(:label_filter_plural) %> + + <%= text_field_tag 'name', params[:name], :size => 30 %> + <%= submit_tag l(:button_apply), :class => "small", :name => nil %> + <%= link_to l(:button_clear), groups_path, :class => 'icon icon-reload' %> +
    +<% end %> +  + <% if @groups.any? %> +
    @@ -12,7 +24,7 @@ <% @groups.each do |group| %> - "> + "> @@ -20,6 +32,8 @@ <% end %>
    <%=l(:label_group)%>
    <%= link_to group, edit_group_path(group) %> <%= (@user_count_by_group_id[group.id] || 0) unless group.builtin? %> <%= delete_link group unless group.builtin? %>
    +
    +<%= pagination_links_full @group_pages, @group_count %> <% else %>

    <%= l(:label_no_data) %>

    <% end %> diff --git a/app/views/groups/new.html.erb b/app/views/groups/new.html.erb index 2a7f0034a..c643161fd 100644 --- a/app/views/groups/new.html.erb +++ b/app/views/groups/new.html.erb @@ -1,6 +1,6 @@ <%= title [l(:label_group_plural), groups_path], l(:label_group_new) %> -<%= labelled_form_for @group do |f| %> +<%= labelled_form_for @group, :html => {:multipart => true} do |f| %> <%= render :partial => 'form', :locals => { :f => f } %>

    <%= f.submit l(:button_create) %> diff --git a/app/views/groups/show.api.rsb b/app/views/groups/show.api.rsb index 15211f2cf..db9dadbd4 100644 --- a/app/views/groups/show.api.rsb +++ b/app/views/groups/show.api.rsb @@ -12,7 +12,7 @@ api.group do end if include_in_api_response?('users') && !@group.builtin? api.array :memberships do - @group.memberships.each do |membership| + @group.memberships.preload(:roles, :project).each do |membership| api.membership do api.id membership.id api.project :id => membership.project.id, :name => membership.project.name @@ -22,7 +22,7 @@ api.group do attrs = {:id => member_role.role.id, :name => member_role.role.name} attrs.merge!(:inherited => true) if member_role.inherited_from.present? api.role attrs - end + end end end end if membership.project diff --git a/app/views/issue_relations/_form.html.erb b/app/views/issue_relations/_form.html.erb index 29b7f0f14..3a1018c1b 100644 --- a/app/views/issue_relations/_form.html.erb +++ b/app/views/issue_relations/_form.html.erb @@ -9,6 +9,6 @@ <%= link_to_function l(:button_cancel), '$("#new-relation-form").hide();'%>

    -<%= javascript_tag "observeAutocompleteField('relation_issue_to_id', '#{escape_javascript auto_complete_issues_path(:project_id => @project, :scope => (Setting.cross_project_issue_relations? ? 'all' : nil))}')" %> +<%= javascript_tag "observeAutocompleteField('relation_issue_to_id', '#{escape_javascript auto_complete_issues_path(:project_id => @project, :scope => (Setting.cross_project_issue_relations? ? 'all' : nil), :issue_id => @issue.id)}')" %> <%= javascript_tag "setPredecessorFieldsVisibility();" %> diff --git a/app/views/issue_statuses/index.html.erb b/app/views/issue_statuses/index.html.erb index 8608402ca..f5e6538c2 100644 --- a/app/views/issue_statuses/index.html.erb +++ b/app/views/issue_statuses/index.html.erb @@ -16,7 +16,7 @@ <% for status in @issue_statuses %> - "> + <%= link_to status.name, edit_issue_status_path(status) %> <% if Issue.use_status_for_done_ratio? %> <%= status.default_done_ratio %> diff --git a/app/views/issues/_attributes.html.erb b/app/views/issues/_attributes.html.erb index 960256e44..640a00e0b 100644 --- a/app/views/issues/_attributes.html.erb +++ b/app/views/issues/_attributes.html.erb @@ -47,7 +47,7 @@
    <% if @issue.safe_attribute? 'parent_issue_id' %>

    <%= f.text_field :parent_issue_id, :size => 10, :required => @issue.required_attribute?('parent_issue_id') %>

    -<%= javascript_tag "observeAutocompleteField('issue_parent_issue_id', '#{escape_javascript auto_complete_issues_path(:project_id => @issue.project, :scope => Setting.cross_project_subtasks)}')" %> +<%= javascript_tag "observeAutocompleteField('issue_parent_issue_id', '#{escape_javascript auto_complete_issues_path(:project_id => @issue.project, :scope => Setting.cross_project_subtasks, :status => @issue.closed? ? 'c' : 'o', :issue_id => @issue.id)}')" %> <% end %> <% if @issue.safe_attribute? 'start_date' %> @@ -65,7 +65,7 @@ <% end %> <% if @issue.safe_attribute? 'estimated_hours' %> -

    <%= f.text_field :estimated_hours, :size => 3, :required => @issue.required_attribute?('estimated_hours') %> <%= l(:field_hours) %>

    +

    <%= f.hours_field :estimated_hours, :size => 3, :required => @issue.required_attribute?('estimated_hours') %> <%= l(:field_hours) %>

    <% end %> <% if @issue.safe_attribute?('done_ratio') && Issue.use_field_for_done_ratio? %> diff --git a/app/views/issues/_changesets.html.erb b/app/views/issues/_changesets.html.erb index f4b47c617..3bd775c06 100644 --- a/app/views/issues/_changesets.html.erb +++ b/app/views/issues/_changesets.html.erb @@ -1,5 +1,5 @@ <% changesets.each do |changeset| %> -
    +

    <%= link_to_revision(changeset, changeset.repository, :text => "#{l(:label_revision)} #{changeset.format_identifier}") %> <% if changeset.filechanges.any? && User.current.allowed_to?(:browse_repository, changeset.project) %> @@ -13,8 +13,8 @@ <% end %>
    <%= authoring(changeset.committed_on, changeset.author) %>

    -
    - <%= textilizable(changeset, :comments) %> +
    + <%= format_changeset_comments changeset %>
    <% end %> diff --git a/app/views/issues/_conflict.html.erb b/app/views/issues/_conflict.html.erb index c13ace665..ea4c35d7d 100644 --- a/app/views/issues/_conflict.html.erb +++ b/app/views/issues/_conflict.html.erb @@ -3,6 +3,7 @@ <% if @conflict_journals.present? %>
    <% @conflict_journals.sort_by(&:id).each do |journal| %> +

    <%= authoring journal.created_on, journal.user, :label => :label_updated_time_by %>

    <% if journal.details.any? %>
      @@ -14,6 +15,7 @@
      <%= textilizable(journal, :notes) unless journal.notes.blank? %>
      +
    <% end %>
    <% end %> diff --git a/app/views/issues/_edit.html.erb b/app/views/issues/_edit.html.erb index 67e33246d..fe2119a07 100644 --- a/app/views/issues/_edit.html.erb +++ b/app/views/issues/_edit.html.erb @@ -14,7 +14,7 @@ <%= labelled_fields_for :time_entry, @time_entry do |time_entry| %>
    -

    <%= time_entry.text_field :hours, :size => 6, :label => :label_spent_time %> <%= l(:field_hours) %>

    +

    <%= time_entry.hours_field :hours, :size => 6, :label => :label_spent_time %> <%= l(:field_hours) %>

    <%= time_entry.select :activity_id, activity_collection_for_select_options %>

    @@ -31,16 +31,36 @@
    <%= l(:field_notes) %> <%= f.text_area :notes, :cols => 60, :rows => 10, :class => 'wiki-edit', :no_label => true %> <%= wikitoolbar_for 'issue_notes' %> - + <% if @issue.safe_attribute? 'private_notes' %> <%= f.check_box :private_notes, :no_label => true %> <% end %> - + <%= call_hook(:view_issues_edit_notes_bottom, { :issue => @issue, :notes => @notes, :form => f }) %>
    - +
    <%= l(:label_attachment_plural) %> -

    <%= render :partial => 'attachments/form', :locals => {:container => @issue} %>

    + <% if @issue.attachments.any? && @issue.safe_attribute?('deleted_attachment_ids') %> +
    <%= link_to l(:label_edit_attachments), '#', :onclick => "$('#existing-attachments').toggle(); return false;" %>
    +
    + <% @issue.attachments.each do |attachment| %> + + <%= text_field_tag '', attachment.filename, :class => "icon icon-attachment filename", :disabled => true %> + + + <% end %> +
    +
    + <% end %> + +
    + <%= render :partial => 'attachments/form', :locals => {:container => @issue} %> +
    <% end %>
    @@ -49,7 +69,7 @@ <%= hidden_field_tag 'last_journal_id', params[:last_journal_id] || @issue.last_journal_id %> <%= submit_tag l(:button_submit) %> <%= preview_link preview_edit_issue_path(:project_id => @project, :id => @issue), 'issue-form' %> - | <%= link_to l(:button_cancel), {}, :onclick => "$('#update').hide(); return false;" %> + | <%= link_to l(:button_cancel), issue_path(id: @issue.id), :onclick => params[:action] == 'show' ? "$('#update').hide(); return false;" : '' %> <%= hidden_field_tag 'prev_issue_id', @prev_issue_id if @prev_issue_id %> <%= hidden_field_tag 'next_issue_id', @next_issue_id if @next_issue_id %> diff --git a/app/views/issues/_form.html.erb b/app/views/issues/_form.html.erb index f12f98af8..ab19feef2 100644 --- a/app/views/issues/_form.html.erb +++ b/app/views/issues/_form.html.erb @@ -1,6 +1,7 @@ <%= labelled_fields_for :issue, @issue do |f| %> <%= call_hook(:view_issues_form_details_top, { :issue => @issue, :form => f }) %> <%= hidden_field_tag 'form_update_triggered_by', '' %> +<%= hidden_field_tag 'back_url', params[:back_url], :id => nil if params[:back_url].present? %> <% if @issue.safe_attribute? 'is_private' %>

    @@ -29,7 +30,7 @@ <%= content_tag 'span', :id => "issue_description_and_toolbar", :style => (@issue.new_record? ? nil : 'display:none') do %> <%= f.text_area :description, :cols => 60, - :rows => (@issue.description.blank? ? 10 : [[10, @issue.description.length / 50].max, 100].min), + :rows => [[10, @issue.description.to_s.length / 50].max, 20].min, :accesskey => accesskey(:edit), :class => 'wiki-edit', :no_label => true %> diff --git a/app/views/issues/_form_custom_fields.html.erb b/app/views/issues/_form_custom_fields.html.erb index 2e12c00e8..13bedd546 100644 --- a/app/views/issues/_form_custom_fields.html.erb +++ b/app/views/issues/_form_custom_fields.html.erb @@ -1,4 +1,7 @@ <% custom_field_values = @issue.editable_custom_field_values %> +<% custom_field_values_full_width = custom_field_values.select { |value| value.custom_field.full_width_layout? } %> +<% custom_field_values -= custom_field_values_full_width %> + <% if custom_field_values.present? %>

    @@ -14,3 +17,7 @@
    <% end %> + +<% custom_field_values_full_width.each do |value| %> +

    <%= custom_field_tag_with_label :issue, value, :required => @issue.required_attribute?(value.custom_field_id) %>

    +<% end %> diff --git a/app/views/issues/_history.html.erb b/app/views/issues/_history.html.erb index 307ac909b..31df3b81d 100644 --- a/app/views/issues/_history.html.erb +++ b/app/views/issues/_history.html.erb @@ -5,7 +5,7 @@

    #<%= journal.indice %> <%= avatar(journal.user, :size => "24") %> <%= authoring journal.created_on, journal.user, :label => :label_updated_time_by %> - <%= content_tag('span', l(:field_is_private), :class => 'private') if journal.private_notes? %>

    + <%= render_private_notes_indicator(journal) %> <% if journal.details.any? %>
      diff --git a/app/views/issues/_list.html.erb b/app/views/issues/_list.html.erb index 0bb2a6747..8024a6948 100644 --- a/app/views/issues/_list.html.erb +++ b/app/views/issues/_list.html.erb @@ -1,7 +1,10 @@ -<%= form_tag({}) do -%> -<%= hidden_field_tag 'back_url', url_for(params), :id => nil %> +<% query_options = nil unless defined?(query_options) %> +<% query_options ||= {} %> + +<%= form_tag({}, :data => {:cm_url => issues_context_menu_path}) do -%> +<%= hidden_field_tag 'back_url', url_for(:params => request.query_parameters), :id => nil %>
      - +
      <% query.inline_columns.each do |column| %> - <%= column_header(column) %> + <%= column_header(query, column, query_options) %> <% end %> - <% grouped_issue_list(issues, @query, @issue_count_by_group) do |issue, level, group_name, group_count, group_totals| -%> + <% grouped_issue_list(issues, query) do |issue, level, group_name, group_count, group_totals| -%> <% if group_name %> <% reset_cycle %> @@ -32,10 +35,15 @@ <%= content_tag('td', column_content(column, issue), :class => column.css_classes) %> <% end %> - <% @query.block_columns.each do |column| + <% query.block_columns.each do |column| if (text = column_content(column, issue)) && text.present? -%> - + <% end -%> <% end -%> diff --git a/app/views/issues/_list_simple.html.erb b/app/views/issues/_list_simple.html.erb deleted file mode 100644 index 9885ef06a..000000000 --- a/app/views/issues/_list_simple.html.erb +++ /dev/null @@ -1,29 +0,0 @@ -<% if issues && issues.any? %> -<%= form_tag({}) do %> -
      @@ -9,12 +12,12 @@ :title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}" %>
      <%= text %> + <% if query.block_columns.count > 1 %> + <%= column.caption %> + <% end %> + <%= text %> +
      - - - - - - - - <% for issue in issues %> - - - - - - - <% end %> - -
      #<%=l(:field_project)%><%=l(:field_tracker)%><%=l(:field_subject)%>
      - <%= check_box_tag("ids[]", issue.id, false, :style => 'display:none;', :id => nil) %> - <%= link_to(issue.id, issue_path(issue)) %> - <%= link_to_project(issue.project) %><%= issue.tracker %> - <%= link_to(issue.subject.truncate(60), issue_path(issue)) %> (<%= issue.status %>) -
      -<% end %> -<% else %> -

      <%= l(:label_no_data) %>

      -<% end %> diff --git a/app/views/issues/_relations.html.erb b/app/views/issues/_relations.html.erb index dcf079564..3825fe64f 100644 --- a/app/views/issues/_relations.html.erb +++ b/app/views/issues/_relations.html.erb @@ -7,30 +7,9 @@

      <%=l(:label_related_issues)%>

      <% if @relations.present? %> -
      - -<% @relations.each do |relation| %> - <% other_issue = relation.other_issue(@issue) -%> - - - - - - - - +<%= form_tag({}, :data => {:cm_url => issues_context_menu_path}) do %> + <%= render_issue_relations(@issue, @relations) %> <% end %> -
      <%= check_box_tag("ids[]", other_issue.id, false, :id => nil) %> - <%= relation.to_s(@issue) {|other| link_to_issue(other, :project => Setting.cross_project_issue_relations?)}.html_safe %> - <%= other_issue.status.name %><%= format_date(other_issue.start_date) %><%= format_date(other_issue.due_date) %><%= link_to(l(:label_relation_delete), - relation_path(relation), - :remote => true, - :method => :delete, - :data => {:confirm => l(:text_are_you_sure)}, - :title => l(:label_relation_delete), - :class => 'icon-only icon-link-break' - ) if User.current.allowed_to?(:manage_issue_relations, @project) %>
      -
      <% end %> <%= form_for @relation, { diff --git a/app/views/issues/_sidebar.html.erb b/app/views/issues/_sidebar.html.erb index df9f43b72..38d682b04 100644 --- a/app/views/issues/_sidebar.html.erb +++ b/app/views/issues/_sidebar.html.erb @@ -6,13 +6,6 @@
    • <%= link_to l(:field_summary), project_issues_report_path(@project) %>
    • <% end %> -<% if User.current.allowed_to?(:view_calendar, @project, :global => true) %> -
    • <%= link_to l(:label_calendar), _project_calendar_path(@project) %>
    • -<% end %> -<% if User.current.allowed_to?(:view_gantt, @project, :global => true) %> -
    • <%= link_to l(:label_gantt), _project_gantt_path(@project) %>
    • -<% end %> - <% if User.current.allowed_to?(:import_issues, @project, :global => true) %>
    • <%= link_to l(:button_import), new_issues_import_path %>
    • <% end %> @@ -21,5 +14,5 @@ <%= call_hook(:view_issues_sidebar_issues_bottom) %> <%= call_hook(:view_issues_sidebar_planning_bottom) %> -<%= render_sidebar_queries %> +<%= render_sidebar_queries(IssueQuery, @project) %> <%= call_hook(:view_issues_sidebar_queries_bottom) %> diff --git a/app/views/issues/_watchers_form.html.erb b/app/views/issues/_watchers_form.html.erb new file mode 100644 index 000000000..b55e60284 --- /dev/null +++ b/app/views/issues/_watchers_form.html.erb @@ -0,0 +1,15 @@ +<% if @issue.safe_attribute? 'watcher_user_ids' -%> + <%= hidden_field_tag 'issue[watcher_user_ids][]', '' %> +

      + + <%= watchers_checkboxes(@issue, users_for_new_issue_watchers(@issue)) %> + + + <%= link_to l(:label_search_for_watchers), + {:controller => 'watchers', :action => 'new', :project_id => @issue.project}, + :class => 'icon icon-add-bullet', + :remote => true, + :method => 'get' %> + +

      +<% end %> diff --git a/app/views/issues/bulk_edit.html.erb b/app/views/issues/bulk_edit.html.erb index 37bdb6d42..7e10d03b6 100644 --- a/app/views/issues/bulk_edit.html.erb +++ b/app/views/issues/bulk_edit.html.erb @@ -43,14 +43,16 @@ <%= select_tag('issue[tracker_id]', content_tag('option', l(:label_no_change_option), :value => '') + - options_from_collection_for_select(@trackers, :id, :name, @issue_params[:tracker_id])) %> + options_from_collection_for_select(@trackers, :id, :name, @issue_params[:tracker_id]), + :onchange => "updateBulkEditFrom('#{escape_javascript url_for(:action => 'bulk_edit', :format => 'js')}')") %>

      <% if @available_statuses.any? %>

      <%= select_tag('issue[status_id]', content_tag('option', l(:label_no_change_option), :value => '') + - options_from_collection_for_select(@available_statuses, :id, :name, @issue_params[:status_id])) %> + options_from_collection_for_select(@available_statuses, :id, :name, @issue_params[:status_id]), + :onchange => "updateBulkEditFrom('#{escape_javascript url_for(:action => 'bulk_edit', :format => 'js')}')") %>

      <% end %> @@ -106,19 +108,30 @@

      <% end %> -<% if @copy && @attachments_present %> -<%= hidden_field_tag 'copy_attachments', '0' %> +<% if @copy && (@attachments_present || @subtasks_present || @watchers_present) %>

      - - <%= check_box_tag 'copy_attachments', '1', params[:copy_attachments] != '0' %> -

      -<% end %> - -<% if @copy && @subtasks_present %> -<%= hidden_field_tag 'copy_subtasks', '0' %> -

      - - <%= check_box_tag 'copy_subtasks', '1', params[:copy_subtasks] != '0' %> + + <% if @attachments_present %> + + <% end %> + <% if @subtasks_present %> + + <% end %> + <% if @watchers_present %> + + <% end %>

      <% end %> @@ -184,6 +197,13 @@
    +<% if @values_by_custom_field.present? %> +
    + <%= l(:warning_fields_cleared_on_bulk_edit) %>:
    + <%= safe_join(@values_by_custom_field.map {|field, ids| content_tag "span", "#{field.name} (#{ids.size})"}, ', ') %> +
    +<% end %> +

    <% if @copy %> <%= hidden_field_tag 'copy', '1' %> diff --git a/app/views/issues/destroy.html.erb b/app/views/issues/destroy.html.erb index 83da014cd..61b841a62 100644 --- a/app/views/issues/destroy.html.erb +++ b/app/views/issues/destroy.html.erb @@ -6,10 +6,13 @@

    <%= l(:text_destroy_time_entries_question, :hours => number_with_precision(@hours, :precision => 2)) %>


    +<% unless Setting.timelog_required_fields.include?('issue_id') %>
    +<% end %> <% if @project %> <%= text_field_tag 'reassign_to_id', params[:reassign_to_id], :size => 6, :onfocus => '$("#todo_reassign").attr("checked", true);' %> +<%= javascript_tag "observeAutocompleteField('reassign_to_id', '#{escape_javascript auto_complete_issues_path(:project_id => @project)}')" %> <% end %>

    diff --git a/app/views/issues/index.api.rsb b/app/views/issues/index.api.rsb index 7660ccbd5..4bba32549 100644 --- a/app/views/issues/index.api.rsb +++ b/app/views/issues/index.api.rsb @@ -26,6 +26,12 @@ api.array :issues, api_meta(:total_count => @issue_count, :offset => @offset, :l api.updated_on issue.updated_on api.closed_on issue.closed_on + api.array :attachments do + issue.attachments.each do |attachment| + render_api_attachment(attachment, api) + end + end if include_in_api_response?('attachments') + api.array :relations do issue.relations.each do |relation| api.relation(:id => relation.id, :issue_id => relation.issue_from_id, :issue_to_id => relation.issue_to_id, :relation_type => relation.relation_type, :delay => relation.delay) diff --git a/app/views/issues/index.html.erb b/app/views/issues/index.html.erb index fd762023f..e055fc88f 100644 --- a/app/views/issues/index.html.erb +++ b/app/views/issues/index.html.erb @@ -7,65 +7,10 @@

    <%= @query.new_record? ? l(:label_issue_plural) : @query.name %>

    <% html_title(@query.new_record? ? l(:label_issue_plural) : @query.name) %> -<%= form_tag({ :controller => 'issues', :action => 'index', :project_id => @project }, - :method => :get, :id => 'query_form') do %> -
    - <%= hidden_field_tag 'set_filter', '1' %> -
    -
    "> - <%= l(:label_filter_plural) %> -
    "> - <%= render :partial => 'queries/filters', :locals => {:query => @query} %> -
    -
    - -
    -

    - <%= link_to_function l(:button_apply), '$("#query_form").submit()', :class => 'icon icon-checked' %> - <%= link_to l(:button_clear), { :set_filter => 1, :project_id => @project }, :class => 'icon icon-reload' %> - <% if @query.new_record? %> - <% if User.current.allowed_to?(:save_queries, @project, :global => true) %> - <%= link_to_function l(:button_save), - "$('#query_form').attr('action', '#{ @project ? new_project_query_path(@project) : new_query_path }').submit()", - :class => 'icon icon-save' %> - <% end %> - <% else %> - <% if @query.editable_by?(User.current) %> - <%= link_to l(:button_edit), edit_query_path(@query), :class => 'icon icon-edit' %> - <%= delete_link query_path(@query) %> - <% end %> - <% end %> -

    -
    +<%= form_tag(_project_issues_path(@project), :method => :get, :id => 'query_form') do %> + <%= render :partial => 'queries/query_form' %> <% end %> -<%= error_messages_for 'query' %> <% if @query.valid? %> <% if @issues.empty? %>

    <%= l(:label_no_data) %>

    @@ -76,22 +21,22 @@ <% end %> <% other_formats_links do |f| %> - <%= f.link_to 'Atom', :url => params.merge(:key => User.current.rss_key) %> - <%= f.link_to 'CSV', :url => params, :onclick => "showModal('csv-export-options', '350px'); return false;" %> - <%= f.link_to 'PDF', :url => params %> + <%= f.link_to_with_query_parameters 'Atom', :key => User.current.rss_key %> + <%= f.link_to_with_query_parameters 'CSV', {}, :onclick => "showModal('csv-export-options', '350px'); return false;" %> + <%= f.link_to_with_query_parameters 'PDF' %> <% end %> <%= submit_tag l(:button_create) %> diff --git a/app/views/issues/new.js.erb b/app/views/issues/new.js.erb index a0c9ad052..c751b5750 100644 --- a/app/views/issues/new.js.erb +++ b/app/views/issues/new.js.erb @@ -1 +1,4 @@ replaceIssueFormWith('<%= escape_javascript(render :partial => 'form') %>'); +<% if params[:form_update_triggered_by] == "issue_project_id" %> +$("#watchers_form_container").html('<%= escape_javascript(render :partial => 'issues/watchers_form') %>'); +<% end %> diff --git a/app/views/issues/show.html.erb b/app/views/issues/show.html.erb index 99bc8424d..f1f279170 100644 --- a/app/views/issues/show.html.erb +++ b/app/views/issues/show.html.erb @@ -11,7 +11,11 @@ :title => "##{@prev_issue_id}", :accesskey => accesskey(:previous) %> | <% if @issue_position && @issue_count %> - <%= l(:label_item_position, :position => @issue_position, :count => @issue_count) %> | + + <%= link_to_if @query_path, + l(:label_item_position, :position => @issue_position, :count => @issue_count), + @query_path %> + | <% end %> <%= link_to_if @next_issue_id, "#{l(:label_next)} \xc2\xbb", @@ -21,7 +25,10 @@ <% end %> - <%= avatar(@issue.author, :size => "50") %> +
    + <%= avatar(@issue.author, :size => "50", :title => l(:field_author)) %> + <%= avatar(@issue.assigned_to, :size => "22", :class => "gravatar gravatar-child", :title => l(:field_assigned_to)) if @issue.assigned_to %> +
    <%= render_issue_subject_with_tree(@issue) %> @@ -39,7 +46,7 @@ rows.left l(:field_priority), @issue.priority.name, :class => 'priority' unless @issue.disabled_core_fields.include?('assigned_to_id') - rows.left l(:field_assigned_to), avatar(@issue.assigned_to, :size => "14").to_s.html_safe + (@issue.assigned_to ? link_to_user(@issue.assigned_to) : "-"), :class => 'assigned-to' + rows.left l(:field_assigned_to), (@issue.assigned_to ? link_to_user(@issue.assigned_to) : "-"), :class => 'assigned-to' end unless @issue.disabled_core_fields.include?('category_id') || (@issue.category.nil? && @issue.project.issue_categories.none?) rows.left l(:field_category), (@issue.category ? @issue.category.name : "-"), :class => 'category' @@ -58,17 +65,13 @@ rows.right l(:field_done_ratio), progress_bar(@issue.done_ratio, :legend => "#{@issue.done_ratio}%"), :class => 'progress' end unless @issue.disabled_core_fields.include?('estimated_hours') - if @issue.estimated_hours.present? || @issue.total_estimated_hours.to_f > 0 - rows.right l(:field_estimated_hours), issue_estimated_hours_details(@issue), :class => 'estimated-hours' - end + rows.right l(:field_estimated_hours), issue_estimated_hours_details(@issue), :class => 'estimated-hours' end - if User.current.allowed_to?(:view_time_entries, @project) - if @issue.total_spent_hours > 0 - rows.right l(:label_spent_time), issue_spent_hours_details(@issue), :class => 'spent-time' - end + if User.current.allowed_to?(:view_time_entries, @project) && @issue.total_spent_hours > 0 + rows.right l(:label_spent_time), issue_spent_hours_details(@issue), :class => 'spent-time' end end %> -<%= render_custom_fields_rows(@issue) %> +<%= render_half_width_custom_fields_rows(@issue) %> <%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %>
    @@ -89,6 +92,8 @@ end %> <%= link_to_attachments @issue, :thumbnails => true %> <% end -%> +<%= render_full_width_custom_fields_rows(@issue) %> + <%= call_hook(:view_issues_show_description_bottom, :issue => @issue) %> <% if !@issue.leaf? || User.current.allowed_to?(:manage_subtasks, @project) %> @@ -98,7 +103,9 @@ end %> <%= link_to_new_subtask(@issue) if User.current.allowed_to?(:manage_subtasks, @project) %>

    <%=l(:label_subtask_plural)%>

    +<%= form_tag({}, :data => {:cm_url => issues_context_menu_path}) do %> <%= render_descendants_tree(@issue) unless @issue.leaf? %> +<% end %> <% end %> @@ -159,4 +166,4 @@ end %> <%= auto_discovery_link_tag(:atom, {:format => 'atom', :key => User.current.rss_key}, :title => "#{@issue.project} - #{@issue.tracker} ##{@issue.id}: #{@issue.subject}") %> <% end %> -<%= context_menu issues_context_menu_path %> +<%= context_menu %> diff --git a/app/views/journals/_notes_form.html.erb b/app/views/journals/_notes_form.html.erb index 41650c5d3..2ab978769 100644 --- a/app/views/journals/_notes_form.html.erb +++ b/app/views/journals/_notes_form.html.erb @@ -2,11 +2,16 @@ :remote => true, :method => 'put', :id => "journal-#{@journal.id}-form") do %> - <%= label_tag "notes", l(:description_notes), :class => "hidden-for-sighted" %> - <%= text_area_tag :notes, @journal.notes, + <%= label_tag "notes", l(:description_notes), :class => "hidden-for-sighted", :for => "journal_#{@journal.id}_notes" %> + <%= text_area_tag 'journal[notes]', @journal.notes, :id => "journal_#{@journal.id}_notes", :class => 'wiki-edit', :rows => (@journal.notes.blank? ? 10 : [[10, @journal.notes.length / 50].max, 100].min) %> + <% if @journal.safe_attribute? 'private_notes' %> + <%= hidden_field_tag 'journal[private_notes]', '0' %> + <%= check_box_tag 'journal[private_notes]', '1', @journal.private_notes, :id => "journal_#{@journal.id}_private_notes" %> + + <% end %> <%= call_hook(:view_journals_notes_form_after_notes, { :journal => @journal}) %>

    <%= submit_tag l(:button_save) %> <%= preview_link preview_edit_issue_path(:project_id => @project, :id => @journal.issue), diff --git a/app/views/journals/new.js.erb b/app/views/journals/new.js.erb index cd6ab0d35..0f832f37f 100644 --- a/app/views/journals/new.js.erb +++ b/app/views/journals/new.js.erb @@ -1,4 +1,9 @@ -$('#issue_notes').val("<%= raw escape_javascript(@content) %>"); +showAndScrollTo("update"); + +var notes = $('#issue_notes').val(); +if (notes > "") { notes = notes + "\n\n"} + +$('#issue_notes').blur().focus().val(notes + "<%= raw escape_javascript(@content) %>"); <% # when quoting a private journal, check the private checkbox if @journal && @journal.private_notes? @@ -6,5 +11,3 @@ $('#issue_notes').val("<%= raw escape_javascript(@content) %>"); $('#issue_private_notes').prop('checked', true); <% end %> -showAndScrollTo("update", "notes"); -$('#notes').scrollTop = $('#notes').scrollHeight - $('#notes').clientHeight; diff --git a/app/views/journals/update.js.erb b/app/views/journals/update.js.erb index 65c198d59..6a297bb96 100644 --- a/app/views/journals/update.js.erb +++ b/app/views/journals/update.js.erb @@ -1,7 +1,9 @@ <% if @journal.frozen? %> $("#change-<%= @journal.id %>").remove(); <% else %> + $("#change-<%= @journal.id %>").attr('class', '<%= @journal.css_classes %>'); $("#journal-<%= @journal.id %>-notes").replaceWith('<%= escape_javascript(render_notes(@journal.issue, @journal, :reply_links => authorize_for('issues', 'edit'))) %>'); + $("#journal-<%= @journal.id %>-private_notes").replaceWith('<%= escape_javascript(render_private_notes_indicator(@journal)) %>'); $("#journal-<%= @journal.id %>-notes").show(); $("#journal-<%= @journal.id %>-form").remove(); <% end %> diff --git a/app/views/layouts/_file.html.erb b/app/views/layouts/_file.html.erb index 7bb939a4c..eb11f9931 100644 --- a/app/views/layouts/_file.html.erb +++ b/app/views/layouts/_file.html.erb @@ -1,10 +1,12 @@ +

    + <%= link_to_attachment @attachment, :text => "#{l(:button_download)} (#{number_to_human_size(@attachment.filesize)})", :download => true, :class => 'icon icon-download' -%> +
    +

    <%=h @attachment.filename %>

    <%= "#{@attachment.description} - " unless @attachment.description.blank? %> <%= link_to_user(@attachment.author) %>, <%= format_time(@attachment.created_on) %>

    -

    <%= link_to_attachment @attachment, :text => l(:button_download), :download => true -%> - (<%= number_to_human_size @attachment.filesize %>)

    <%= yield %> diff --git a/app/views/layouts/base.html.erb b/app/views/layouts/base.html.erb index 690dbeccd..3fba9b9ee 100644 --- a/app/views/layouts/base.html.erb +++ b/app/views/layouts/base.html.erb @@ -75,6 +75,7 @@ <% if User.current.logged? || !Setting.login_required? %>
    - <%= l(:label_role_plural) %> <%= toggle_checkboxes_link('.roles-selection input') %> + <%= toggle_checkboxes_link('.roles-selection input') %><%= l(:label_role_plural) %>
    <% User.current.managed_roles(@project).each do |role| %> diff --git a/app/views/members/edit.html.erb b/app/views/members/edit.html.erb new file mode 100644 index 000000000..99ef3161d --- /dev/null +++ b/app/views/members/edit.html.erb @@ -0,0 +1,3 @@ +<%= title "#{@member.principal} - #{@member.project}" %> + +<%= render :partial => 'edit' %> diff --git a/app/views/members/edit.js.erb b/app/views/members/edit.js.erb new file mode 100644 index 000000000..379ed3ece --- /dev/null +++ b/app/views/members/edit.js.erb @@ -0,0 +1,3 @@ +$("#member-<%= @member.id %>-roles").hide(); +$("#member-<%= @member.id %>-form").html("<%= escape_javascript(render :partial => "edit") %>"); + diff --git a/app/views/messages/show.html.erb b/app/views/messages/show.html.erb index 60c7d5f99..180a2ba7d 100644 --- a/app/views/messages/show.html.erb +++ b/app/views/messages/show.html.erb @@ -29,12 +29,15 @@
    <%= textilizable(@topic, :content) %>
    -<%= link_to_attachments @topic, :author => false %> +<%= link_to_attachments @topic, :author => false, :thumbnails => true %>

    <% unless @replies.empty? %> -

    <%= l(:label_reply_plural) %> (<%= @reply_count %>)

    +

    <%= l(:label_reply_plural) %> (<%= @reply_count %>)

    +<% if !@topic.locked? && authorize_for('messages', 'reply') && @replies.size >= 3 %> +

    <%= toggle_link l(:button_reply), "reply", :focus => 'message_content', :scroll => "message_content" %>

    +<% end %> <% @replies.each do |message| %>
    ">
    @@ -68,7 +71,7 @@ <%= authoring message.created_on, message.author %>
    <%= textilizable message, :content, :attachments => message.attachments %>
    - <%= link_to_attachments message, :author => false %> + <%= link_to_attachments message, :author => false, :thumbnails => true %>
    <% end %> <%= pagination_links_full @reply_pages, @reply_count, :per_page_links => false %> diff --git a/app/views/my/_block.html.erb b/app/views/my/_block.html.erb deleted file mode 100644 index 813eb2b88..000000000 --- a/app/views/my/_block.html.erb +++ /dev/null @@ -1,10 +0,0 @@ -
    - -
    - <%= link_to "", {:action => "remove_block", :block => block_name}, :method => 'post', :class => "close-icon" %> -
    - -
    - <%= render :partial => "my/blocks/#{block_name}", :locals => { :user => user } %> -
    -
    diff --git a/app/views/my/account.html.erb b/app/views/my/account.html.erb index ca8a1441e..44c75f2a3 100644 --- a/app/views/my/account.html.erb +++ b/app/views/my/account.html.erb @@ -14,7 +14,7 @@ <%= labelled_form_for :user, @user, :url => { :action => "account" }, :html => { :id => 'my_account_form', - :method => :post } do |f| %> + :method => :post, :multipart => true } do |f| %>
    <%=l(:label_information_plural)%> diff --git a/app/views/my/add_block.js.erb b/app/views/my/add_block.js.erb new file mode 100644 index 000000000..c2382ee7a --- /dev/null +++ b/app/views/my/add_block.js.erb @@ -0,0 +1,3 @@ +$("#block-<%= escape_javascript @block %>").remove(); +$("#list-top").prepend("<%= escape_javascript render_blocks([@block], @user) %>"); +$("#block-select").replaceWith("<%= escape_javascript block_select_tag(@user) %>"); diff --git a/app/views/my/blocks/_calendar.html.erb b/app/views/my/blocks/_calendar.html.erb index 89dde0e01..c85190f2a 100644 --- a/app/views/my/blocks/_calendar.html.erb +++ b/app/views/my/blocks/_calendar.html.erb @@ -1,6 +1,3 @@

    <%= l(:label_calendar) %>

    -<% calendar = Redmine::Helpers::Calendar.new(User.current.today, current_language, :week) - calendar.events = calendar_items(calendar.startdt, calendar.enddt) %> - <%= render :partial => 'common/calendar', :locals => {:calendar => calendar } %> diff --git a/app/views/my/blocks/_documents.html.erb b/app/views/my/blocks/_documents.html.erb index a55094cf1..560adca1e 100644 --- a/app/views/my/blocks/_documents.html.erb +++ b/app/views/my/blocks/_documents.html.erb @@ -1,3 +1,3 @@

    <%=l(:label_document_plural)%>

    -<%= render :partial => 'documents/document', :collection => documents_items %> +<%= render :partial => 'documents/document', :collection => documents %> diff --git a/app/views/my/blocks/_issue_query_selection.html.erb b/app/views/my/blocks/_issue_query_selection.html.erb new file mode 100644 index 000000000..640f049b0 --- /dev/null +++ b/app/views/my/blocks/_issue_query_selection.html.erb @@ -0,0 +1,19 @@ +

    + <%= l(:label_issue_plural) %> +

    + +
    + <%= form_tag(my_page_path, :remote => true) do %> +
    +

    + +

    +
    +

    + <%= submit_tag l(:button_save) %> +

    + <% end %> +
    diff --git a/app/views/my/blocks/_issues.erb b/app/views/my/blocks/_issues.erb new file mode 100644 index 000000000..845ef5b85 --- /dev/null +++ b/app/views/my/blocks/_issues.erb @@ -0,0 +1,41 @@ +
    + <%= link_to_function l(:label_options), "$('##{block}-settings').toggle();", :class => 'icon-only icon-settings', :title => l(:label_options) %> +
    + +

    + <%= "#{query.project} |" if query.project %> + <%= link_to query.name, _project_issues_path(query.project, query.as_params) %> + (<%= query.issue_count %>) +

    + + + +<% if issues.any? %> + <%= render :partial => 'issues/list', + :locals => { + :issues => issues, + :query => query, + :query_options => { + :sort_param => "settings[#{block}][sort]", + :sort_link_options => {:method => :post, :remote => true} + } + } %> +<% else %> +

    <%= l(:label_no_data) %>

    +<% end %> + +<% content_for :header_tags do %> +<%= auto_discovery_link_tag(:atom, + _project_issues_path(query.project, query.as_params.merge(:format => 'atom', :key => User.current.rss_key)), + {:title => query.name}) %> +<% end %> diff --git a/app/views/my/blocks/_issuesassignedtome.html.erb b/app/views/my/blocks/_issuesassignedtome.html.erb deleted file mode 100644 index 94948b7aa..000000000 --- a/app/views/my/blocks/_issuesassignedtome.html.erb +++ /dev/null @@ -1,15 +0,0 @@ -<% assigned_issues = issuesassignedtome_items %> -

    - <%= link_to l(:label_assigned_to_me_issues), - issues_path(:set_filter => 1, :assigned_to_id => 'me', :sort => 'priority:desc,updated_on:desc') %> - (<%= assigned_issues.limit(nil).count %>) -

    - -<%= render :partial => 'issues/list_simple', :locals => { :issues => assigned_issues.to_a } %> - -<% content_for :header_tags do %> -<%= auto_discovery_link_tag(:atom, - {:controller => 'issues', :action => 'index', :set_filter => 1, - :assigned_to_id => 'me', :format => 'atom', :key => User.current.rss_key}, - {:title => l(:label_assigned_to_me_issues)}) %> -<% end %> diff --git a/app/views/my/blocks/_issuesreportedbyme.html.erb b/app/views/my/blocks/_issuesreportedbyme.html.erb deleted file mode 100644 index 91557c035..000000000 --- a/app/views/my/blocks/_issuesreportedbyme.html.erb +++ /dev/null @@ -1,15 +0,0 @@ -<% reported_issues = issuesreportedbyme_items %> -

    - <%= link_to l(:label_reported_issues), - issues_path(:set_filter => 1, :status_id => 'o', :author_id => 'me', :sort => 'updated_on:desc') %> - (<%= reported_issues.limit(nil).count %>) -

    - -<%= render :partial => 'issues/list_simple', :locals => { :issues => reported_issues.to_a } %> - -<% content_for :header_tags do %> -<%= auto_discovery_link_tag(:atom, - {:controller => 'issues', :action => 'index', :set_filter => 1, - :author_id => 'me', :format => 'atom', :key => User.current.rss_key}, - {:title => l(:label_reported_issues)}) %> -<% end %> diff --git a/app/views/my/blocks/_issueswatched.html.erb b/app/views/my/blocks/_issueswatched.html.erb deleted file mode 100644 index 512a52238..000000000 --- a/app/views/my/blocks/_issueswatched.html.erb +++ /dev/null @@ -1,9 +0,0 @@ -<% watched_issues = issueswatched_items %> -

    - <%= link_to l(:label_watched_issues), - issues_path(:set_filter => 1, :watcher_id => 'me', :sort => 'updated_on:desc') %> - (<%= watched_issues.limit(nil).count %>) -

    - - -<%= render :partial => 'issues/list_simple', :locals => { :issues => watched_issues.to_a } %> diff --git a/app/views/my/blocks/_news.html.erb b/app/views/my/blocks/_news.html.erb index c478e4196..62a618617 100644 --- a/app/views/my/blocks/_news.html.erb +++ b/app/views/my/blocks/_news.html.erb @@ -1,3 +1,3 @@

    <%=l(:label_news_latest)%>

    -<%= render :partial => 'news/news', :collection => news_items %> +<%= render :partial => 'news/news', :collection => news %> diff --git a/app/views/my/blocks/_timelog.html.erb b/app/views/my/blocks/_timelog.html.erb index 14a0711e9..350cbaec8 100644 --- a/app/views/my/blocks/_timelog.html.erb +++ b/app/views/my/blocks/_timelog.html.erb @@ -1,59 +1,63 @@ +
    + <%= link_to_function l(:label_options), "$('#timelog-settings').toggle();", :class => 'icon-only icon-settings', :title => l(:label_options) %> +
    +

    <%= link_to l(:label_spent_time), time_entries_path(:user_id => 'me') %> - (<%= l(:label_last_n_days, 7) %>) + (<%= l(:label_last_n_days, days) %>: <%= l_hours_short entries.sum(&:hours) %>) + <%= link_to l(:button_log_time), new_time_entry_path, :class => "icon-only icon-add", :title => l(:button_log_time) if User.current.allowed_to?(:log_time, nil, :global => true) %>

    -<% -entries = timelog_items -entries_by_day = entries.group_by(&:spent_on) -%> -<% if User.current.allowed_to?(:log_time, nil, :global => true) %> -
    - <%= link_to l(:button_log_time), new_time_entry_path, :class => "icon icon-add" %> -
    -<% end %> -
    -

    <%= l(:label_total_time) %>: <%= html_hours("%.2f" % entries.sum(&:hours).to_f) %>

    + <% if entries.any? %> - +<%= form_tag({}, :data => {:cm_url => time_entries_context_menu_path}) do %> +
    - <% entries_by_day.keys.sort.reverse.each do |day| %> - + - - - - <% entries_by_day[day].each do |entry| -%> - - + + + <% entries_by_day[day].each do |entry| -%> + + - - - - <% end -%> + + + <% end -%> <% end -%>
    <%= l(:label_activity) %> <%= l(:label_project) %> <%= l(:field_comments) %> <%= l(:field_hours) %>
    <%= day == User.current.today ? l(:label_today).titleize : format_date(day) %> <%= html_hours("%.2f" % entries_by_day[day].sum(&:hours).to_f) %>
    <%= entry.activity %><%= html_hours(format_hours(entries_by_day[day].sum(&:hours))) %>
    + <%= check_box_tag("ids[]", entry.id, false, :style => 'display:none;', :id => nil) %> + <%= entry.activity %> + <%= entry.project %> <%= h(' - ') + link_to_issue(entry.issue, :truncate => 50) if entry.issue %> <%= entry.comments %><%= html_hours("%.2f" % entry.hours) %> - <% if entry.editable_by?(@user) -%> - <%= link_to l(:button_edit), {:controller => 'timelog', :action => 'edit', :id => entry}, - :title => l(:button_edit), - :class => 'icon-only icon-edit' %> - <%= link_to l(:button_delete), {:controller => 'timelog', :action => 'destroy', :id => entry}, - :data => {:confirm => l(:text_are_you_sure)}, :method => :delete, - :title => l(:button_delete), - :class => 'icon-only icon-del' %> - <% end -%> -
    <%= html_hours(format_hours(entry.hours)) %>
    <% end %> +<% else %> +

    <%= l(:label_no_data) %>

    +<% end %> diff --git a/app/views/my/page.html.erb b/app/views/my/page.html.erb index 819dddeaf..ba150fd79 100644 --- a/app/views/my/page.html.erb +++ b/app/views/my/page.html.erb @@ -1,36 +1,46 @@
    - <%= link_to l(:label_personalize_page), :action => 'page_layout' %> + <%= form_tag({:action => "add_block"}, :remote => true, :id => "block-form") do %> + <%= label_tag('block-select', l(:button_add)) %>: + <%= block_select_tag(@user) %> + <% end %>

    <%=l(:label_my_page)%>

    -
    - <% @blocks['top'].each do |b| - next unless MyController::BLOCKS.keys.include? b %> -
    - <%= render :partial => "my/blocks/#{b}", :locals => { :user => @user } %> +
    +<% @groups.each do |group| %> +
    + <%= render_blocks(@blocks[group], @user) %>
    - <% end if @blocks['top'] %> +<% end %>
    -
    - <% @blocks['left'].each do |b| - next unless MyController::BLOCKS.keys.include? b %> -
    - <%= render :partial => "my/blocks/#{b}", :locals => { :user => @user } %> -
    - <% end if @blocks['left'] %> -
    +<%= context_menu %> -
    - <% @blocks['right'].each do |b| - next unless MyController::BLOCKS.keys.include? b %> -
    - <%= render :partial => "my/blocks/#{b}", :locals => { :user => @user } %> -
    - <% end if @blocks['right'] %> -
    - -<%= context_menu issues_context_menu_path %> +<%= javascript_tag do %> +$(document).ready(function(){ + $('#block-select').val(''); + $('.block-receiver').sortable({ + connectWith: '.block-receiver', + tolerance: 'pointer', + handle: '.sort-handle', + start: function(event, ui){$(this).parent().addClass('dragging');}, + stop: function(event, ui){$(this).parent().removeClass('dragging');}, + update: function(event, ui){ + // trigger the call on the list that receives the block only + if ($(this).find(ui.item).length > 0) { + $.ajax({ + url: "<%= escape_javascript url_for(:action => "order_blocks") %>", + type: 'post', + data: { + 'group': $(this).attr('id').replace(/^list-/, ''), + 'blocks': $.map($(this).children(), function(el){return $(el).attr('id').replace(/^block-/, '');}) + } + }); + } + } + }); +}); +<% end %> <% html_title(l(:label_my_page)) -%> diff --git a/app/views/my/page_layout.html.erb b/app/views/my/page_layout.html.erb deleted file mode 100644 index 4a60d97e1..000000000 --- a/app/views/my/page_layout.html.erb +++ /dev/null @@ -1,41 +0,0 @@ -
    -<% if @block_options.present? %> - <%= form_tag({:action => "add_block"}, :id => "block-form") do %> - <%= label_tag('block-select', l(:label_my_page_block)) %>: - <%= select_tag 'block', - content_tag('option') + options_for_select(@block_options), - :id => "block-select" %> - <%= link_to l(:button_add), '#', :onclick => '$("#block-form").submit()', :class => 'icon icon-add' %> - <% end %> -<% end %> -<%= link_to l(:button_back), {:action => 'page'}, :class => 'icon icon-cancel' %> -
    - -

    <%=l(:label_my_page)%>

    - -
    - <% @blocks['top'].each do |b| - next unless MyController::BLOCKS.keys.include? b %> - <%= render :partial => 'block', :locals => {:user => @user, :block_name => b} %> - <% end if @blocks['top'] %> -
    - -
    - <% @blocks['left'].each do |b| - next unless MyController::BLOCKS.keys.include? b %> - <%= render :partial => 'block', :locals => {:user => @user, :block_name => b} %> - <% end if @blocks['left'] %> -
    - -
    - <% @blocks['right'].each do |b| - next unless MyController::BLOCKS.keys.include? b %> - <%= render :partial => 'block', :locals => {:user => @user, :block_name => b} %> - <% end if @blocks['right'] %> -
    - -<%= javascript_tag "initMyPageSortable('top', '#{ escape_javascript url_for(:action => "order_blocks", :group => "top") }');" %> -<%= javascript_tag "initMyPageSortable('left', '#{ escape_javascript url_for(:action => "order_blocks", :group => "left") }');" %> -<%= javascript_tag "initMyPageSortable('right', '#{ escape_javascript url_for(:action => "order_blocks", :group => "right") }');" %> - -<% html_title(l(:label_my_page)) -%> diff --git a/app/views/my/remove_block.js.erb b/app/views/my/remove_block.js.erb new file mode 100644 index 000000000..275e9e1e2 --- /dev/null +++ b/app/views/my/remove_block.js.erb @@ -0,0 +1,2 @@ +$("#block-<%= escape_javascript @block %>").remove(); +$("#block-select").replaceWith("<%= escape_javascript block_select_tag(@user) %>"); diff --git a/app/views/my/update_page.js.erb b/app/views/my/update_page.js.erb new file mode 100644 index 000000000..d51ab8105 --- /dev/null +++ b/app/views/my/update_page.js.erb @@ -0,0 +1,3 @@ +<% @updated_blocks.each do |block| %> + $("#block-<%= block %>").replaceWith("<%= escape_javascript render_block(block.to_s, @user) %>"); +<% end %> diff --git a/app/views/news/index.html.erb b/app/views/news/index.html.erb index 64614c154..2ff5a1c8c 100644 --- a/app/views/news/index.html.erb +++ b/app/views/news/index.html.erb @@ -44,7 +44,7 @@ <% end %> <% content_for :header_tags do %> - <%= auto_discovery_link_tag(:atom, params.merge({:format => 'atom', :page => nil, :key => User.current.rss_key})) %> + <%= auto_discovery_link_tag(:atom, _project_news_path(@project, :key => User.current.rss_key, :format => 'atom')) %> <%= stylesheet_link_tag 'scm' %> <% end %> diff --git a/app/views/news/show.html.erb b/app/views/news/show.html.erb index 2178e1c14..72c94a9c7 100644 --- a/app/views/news/show.html.erb +++ b/app/views/news/show.html.erb @@ -8,7 +8,7 @@ <%= delete_link news_path(@news) if User.current.allowed_to?(:manage_news, @project) %>
    -

    <%= avatar(@news.author, :size => "24") %><%=h @news.title %>

    +

    <%= avatar(@news.author, :size => "24") %> <%=h @news.title %>

    <% if authorize_for('news', 'edit') %>
<% end %> -

- - <%= authoring(@changeset.committed_on, @changeset.author) %> - -

-<%= textilizable @changeset.comments %> +
+ <%= format_changeset_comments @changeset %> +
<% if @changeset.issues.visible.any? || User.current.allowed_to?(:manage_related_issues, @repository.project) %> <%= render :partial => 'related_issues' %> diff --git a/app/views/repositories/_dir_list_content.html.erb b/app/views/repositories/_dir_list_content.html.erb index 9721fa66c..f78073b0c 100644 --- a/app/views/repositories/_dir_list_content.html.erb +++ b/app/views/repositories/_dir_list_content.html.erb @@ -17,7 +17,7 @@ :parent_id => tr_id)) %>');">  <% end %> <%= link_to ent_name, - {:action => (entry.is_dir? ? 'show' : 'changes'), :id => @project, :repository_id => @repository.identifier_param, :path => to_path_param(ent_path), :rev => @rev}, + {:action => (entry.is_dir? ? 'show' : 'entry'), :id => @project, :repository_id => @repository.identifier_param, :path => to_path_param(ent_path), :rev => @rev}, :class => (entry.is_dir? ? 'icon icon-folder' : "icon icon-file #{Redmine::MimeType.css_class_of(ent_name)}")%> <%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %> diff --git a/app/views/repositories/_link_to_functions.html.erb b/app/views/repositories/_link_to_functions.html.erb index f75a7ce5f..9c89561c3 100644 --- a/app/views/repositories/_link_to_functions.html.erb +++ b/app/views/repositories/_link_to_functions.html.erb @@ -1,19 +1,19 @@ <% if @entry && @entry.kind == 'file' %> -

-<%= link_to_if action_name != 'changes', l(:label_history), {:action => 'changes', :id => @project, :repository_id => @repository.identifier_param, :path => to_path_param(@path), :rev => @rev } %> | -<% if @repository.supports_cat? %> - <%= link_to_if action_name != 'entry', l(:button_view), {:action => 'entry', :id => @project, :repository_id => @repository.identifier_param, :path => to_path_param(@path), :rev => @rev } %> | -<% end %> -<% if @repository.supports_annotate? %> - <%= link_to_if action_name != 'annotate', l(:button_annotate), {:action => 'annotate', :id => @project, :repository_id => @repository.identifier_param, :path => to_path_param(@path), :rev => @rev } %> | -<% end %> -<%= link_to(l(:button_download), - {:action => 'raw', :id => @project, - :repository_id => @repository.identifier_param, - :path => to_path_param(@path), - :rev => @rev}) if @repository.supports_cat? %> -<%= "(#{number_to_human_size(@entry.size)})" if @entry.size %> -

+<% +tabs = [] +tabs << { name: 'entry', label: :button_view, + url: {:action => 'entry', :id => @project, :repository_id => @repository.identifier_param, :path => to_path_param(@path), :rev => @rev } + } if @repository.supports_cat? + +tabs << { name: 'changes', label: :label_history, + url: {:action => 'changes', :id => @project, :repository_id => @repository.identifier_param, :path => to_path_param(@path), :rev => @rev } + } +tabs << { name: 'annotate', label: :button_annotate, + url: {:action => 'annotate', :id => @project, :repository_id => @repository.identifier_param, :path => to_path_param(@path), :rev => @rev } + } if @repository.supports_annotate? +%> + +<%= render :partial => 'common/tabs', :locals => {:tabs => tabs, :selected_tab => action_name} %> <% end %> diff --git a/app/views/repositories/_navigation.html.erb b/app/views/repositories/_navigation.html.erb index 082f55575..0bd6e33cf 100644 --- a/app/views/repositories/_navigation.html.erb +++ b/app/views/repositories/_navigation.html.erb @@ -2,6 +2,15 @@ <%= javascript_include_tag 'repository_navigation' %> <% end %> +<% if @entry && !@entry.is_dir? && @repository.supports_cat? %> + <% download_label = @entry.size ? "#{l :button_download} (#{number_to_human_size @entry.size})" : l(:button_download) %> + <%= link_to(download_label, + {:action => 'raw', :id => @project, + :repository_id => @repository.identifier_param, + :path => to_path_param(@path), + :rev => @rev}, class: 'icon icon-download') %> +<% end %> + <%= link_to l(:label_statistics), {:action => 'stats', :id => @project, :repository_id => @repository.identifier_param}, :class => 'icon icon-stats' if @repository.supports_all_revisions? %> diff --git a/app/views/repositories/_revisions.html.erb b/app/views/repositories/_revisions.html.erb index a8e0197f5..72f97c06b 100644 --- a/app/views/repositories/_revisions.html.erb +++ b/app/views/repositories/_revisions.html.erb @@ -33,7 +33,7 @@ end %> <% show_diff = revisions.size > 1 %> <% line_num = 1 %> <% revisions.each do |changeset| %> - + <% id_style = (show_revision_graph ? "padding-left:#{(graph_space + 1) * 20}px" : nil) %> <%= content_tag(:td, :class => 'id', :style => id_style) do %> <%= link_to_revision(changeset, @repository) %> @@ -41,8 +41,8 @@ end %> <%= radio_button_tag('rev', changeset.identifier, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('#cbto-#{line_num+1}').prop('checked',true);") if show_diff && (line_num < revisions.size) %> <%= radio_button_tag('rev_to', changeset.identifier, (line_num==2), :id => "cbto-#{line_num}", :onclick => "if ($('#cb-#{line_num}').prop('checked')) {$('#cb-#{line_num-1}').prop('checked',true);}") if show_diff && (line_num > 1) %> <%= format_time(changeset.committed_on) %> -<%= changeset.author.to_s.truncate(30) %> -<%= textilizable(truncate_at_line_break(changeset.comments)) %> +<%= changeset.user.blank? ? changeset.author.to_s.truncate(30) : link_to_user(changeset.user) %> +<%= textilizable(truncate_at_line_break(changeset.comments), :formatting => Setting.commit_logs_formatting?) %> <% line_num += 1 %> <% end %> diff --git a/app/views/repositories/annotate.html.erb b/app/views/repositories/annotate.html.erb index fcb6d3d84..79a548f7e 100644 --- a/app/views/repositories/annotate.html.erb +++ b/app/views/repositories/annotate.html.erb @@ -8,8 +8,8 @@ <%= render :partial => 'link_to_functions' %> +<% if @annotate %> <% colors = Hash.new {|k,v| k[v] = (k.size % 12) } %> -
@@ -38,6 +38,9 @@
+<% else %> +

<%= @error_message %>

+<% end %> <% html_title(l(:button_annotate)) -%> diff --git a/app/views/repositories/committers.html.erb b/app/views/repositories/committers.html.erb index b942e4b82..f23e10252 100644 --- a/app/views/repositories/committers.html.erb +++ b/app/views/repositories/committers.html.erb @@ -17,7 +17,7 @@ <% i = 0 -%> <% @committers.each do |committer, user_id| -%> - + <%= committer %> <%= hidden_field_tag "committers[#{i}][]", committer, :id => nil %> diff --git a/app/views/repositories/diff.html.erb b/app/views/repositories/diff.html.erb index 5d0b899a7..889a65f7e 100644 --- a/app/views/repositories/diff.html.erb +++ b/app/views/repositories/diff.html.erb @@ -21,7 +21,7 @@ <% end -%> <% other_formats_links do |f| %> - <%= f.link_to 'Diff', :url => params, :caption => 'Unified diff' %> + <%= f.link_to_with_query_parameters 'Diff', {}, :caption => 'Unified diff' %> <% end %> <% html_title(with_leading_slash(@path), 'Diff') -%> diff --git a/app/views/repositories/entry.html.erb b/app/views/repositories/entry.html.erb index 37a5db961..16bebc79c 100644 --- a/app/views/repositories/entry.html.erb +++ b/app/views/repositories/entry.html.erb @@ -9,11 +9,23 @@ <%= render :partial => 'link_to_functions' %> <% if Redmine::MimeType.is_type?('image', @path) %> - <%= render :partial => 'common/image', :locals => {:path => url_for(params.merge(:action => 'raw')), :alt => @path} %> + <%= render :partial => 'common/image', :locals => {:path => url_for(:action => 'raw', + :id => @project, + :repository_id => @repository.identifier_param, + :path => @path, + :rev => @rev), :alt => @path} %> <% elsif @content %> <%= render :partial => 'common/file', :locals => {:filename => @path, :content => @content} %> <% else %> - <%= render :partial => 'common/other' %> + <%= render :partial => 'common/other', + :locals => { + :download_link => @repository.supports_cat? ? link_to( + l(:label_no_preview_download), + { :action => 'raw', :id => @project, + :repository_id => @repository.identifier_param, + :path => to_path_param(@path), + :rev => @rev }, + :class => 'icon icon-download') : nil } %> <% end %> <% content_for :header_tags do %> diff --git a/app/views/repositories/revisions.html.erb b/app/views/repositories/revisions.html.erb index c0afb955a..fc22210da 100644 --- a/app/views/repositories/revisions.html.erb +++ b/app/views/repositories/revisions.html.erb @@ -23,8 +23,8 @@ <%= stylesheet_link_tag "scm" %> <%= auto_discovery_link_tag( :atom, - params.merge( - {:format => 'atom', :page => nil, :key => User.current.rss_key})) %> + :params => request.query_parameters.merge(:page => nil, :key => User.current.rss_key), + :format => 'atom') %> <% end %> <% other_formats_links do |f| %> diff --git a/app/views/repositories/show.html.erb b/app/views/repositories/show.html.erb index b89bbf9fe..d96a737ae 100644 --- a/app/views/repositories/show.html.erb +++ b/app/views/repositories/show.html.erb @@ -42,9 +42,10 @@ <% if @repository.supports_all_revisions? %> <% content_for :header_tags do %> <%= auto_discovery_link_tag( - :atom, params.merge( - {:format => 'atom', :action => 'revisions', - :id => @project, :page => nil, :key => User.current.rss_key})) %> + :atom, + :action => 'revisions', :id => @project, + :repository_id => @repository.identifier_param, + :key => User.current.rss_key) %> <% end %> <% other_formats_links do |f| %> diff --git a/app/views/roles/_form.html.erb b/app/views/roles/_form.html.erb index 39acc34a4..5f095e7b2 100644 --- a/app/views/roles/_form.html.erb +++ b/app/views/roles/_form.html.erb @@ -91,7 +91,7 @@ <% end %> <% Tracker.sorted.all.each do |tracker| %> - "> + <%= tracker.name %> <% permissions.each do |permission| %> "><%= check_box_tag "role[permissions_tracker_ids][#{permission}][]", diff --git a/app/views/roles/index.html.erb b/app/views/roles/index.html.erb index 8f059648b..ab0bbe8da 100644 --- a/app/views/roles/index.html.erb +++ b/app/views/roles/index.html.erb @@ -12,7 +12,7 @@ <% for role in @roles %> - <%= role.builtin? ? "builtin" : "givable" %>"> + "> <%= content_tag(role.builtin? ? 'em' : 'span', link_to(role.name, edit_role_path(role))) %> <%= reorder_handle(role) unless role.builtin? %> diff --git a/app/views/roles/permissions.html.erb b/app/views/roles/permissions.html.erb index b403cffe1..a4b14493d 100644 --- a/app/views/roles/permissions.html.erb +++ b/app/views/roles/permissions.html.erb @@ -33,20 +33,23 @@ <% end %> <% perms_by_module[mod].each do |permission| %> - + <% humanized_perm_name = l_or_humanize(permission.name, :prefix => 'permission_') %> + <%= link_to_function('', "toggleCheckboxesBySelector('.permission-#{permission.name} input')", :title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}", :class => 'icon-only icon-checked') %> - <%= l_or_humanize(permission.name, :prefix => 'permission_') %> + <%= humanized_perm_name %> <% @roles.each do |role| %> - - <% if role.setable_permissions.include? permission %> - <%= check_box_tag "permissions[#{role.id}][]", permission.name, (role.permissions.include? permission.name), :id => nil, :class => "role-#{role.id}" %> - <% end %> - + <% if role.setable_permissions.include? permission %> + "> + <%= check_box_tag "permissions[#{role.id}][]", permission.name, (role.permissions.include? permission.name), :id => nil, :class => "role-#{role.id}" %> + + <% else %> + + <% end %> <% end %> <% end %> diff --git a/app/views/search/index.html.erb b/app/views/search/index.html.erb index 53ec17a63..1e80d57a7 100644 --- a/app/views/search/index.html.erb +++ b/app/views/search/index.html.erb @@ -42,7 +42,7 @@

<%= l(:label_result_plural) %> (<%= @result_count %>)

<% @results.each do |e| %> -
+
<%= content_tag('span', e.project, :class => 'project') unless @project == e.project %> <%= link_to(highlight_tokens(e.event_title.truncate(255), @tokens), e.event_url) %>
diff --git a/app/views/settings/_authentication.html.erb b/app/views/settings/_authentication.html.erb index 15367813a..4454bbae4 100644 --- a/app/views/settings/_authentication.html.erb +++ b/app/views/settings/_authentication.html.erb @@ -8,7 +8,12 @@

<%= setting_select :self_registration, [[l(:label_disabled), "0"], [l(:label_registration_activation_by_email), "1"], [l(:label_registration_manual_activation), "2"], - [l(:label_registration_automatic_activation), "3"]] %>

+ [l(:label_registration_automatic_activation), "3"]], + :onchange => + "if (this.value != '0') { $('#settings_show_custom_fields_on_registration').removeAttr('disabled'); } else { $('#settings_show_custom_fields_on_registration').attr('disabled', true); }" %>

+ +

<%= setting_check_box :show_custom_fields_on_registration, + :disabled => !Setting.self_registration? %>

<%= setting_check_box :unsubscribe %>

@@ -41,6 +46,8 @@

<%= setting_check_box :default_users_hide_mail, :label => :field_hide_mail %>

+ +

<%= setting_select :default_users_time_zone, ActiveSupport::TimeZone.all.collect {|z| [ z.to_s, z.name ]}, :label => :field_time_zone, :blank => :label_none %>

diff --git a/app/views/settings/_display.html.erb b/app/views/settings/_display.html.erb index be3e70e34..93eaa6ef4 100644 --- a/app/views/settings/_display.html.erb +++ b/app/views/settings/_display.html.erb @@ -15,6 +15,8 @@

<%= setting_select :time_format, Setting::TIME_FORMATS.collect {|f| [::I18n.l(Time.now, :locale => locale, :format => f), f]}, :blank => :label_language_based %>

+

<%= setting_select :timespan_format, [["%.2f" % 0.75, 'decimal'], ['0:45 h', 'minutes']], :blank => false %>

+

<%= setting_select :user_format, @options[:user_format] %>

<%= setting_check_box :gravatar_enabled %>

diff --git a/app/views/settings/_mail_handler.html.erb b/app/views/settings/_mail_handler.html.erb index f255b4adc..3e1a40d1d 100644 --- a/app/views/settings/_mail_handler.html.erb +++ b/app/views/settings/_mail_handler.html.erb @@ -3,6 +3,10 @@

<%= setting_text_area :mail_handler_body_delimiters, :rows => 5 %> + <%= l(:text_line_separated) %>

diff --git a/app/views/settings/_repositories.html.erb b/app/views/settings/_repositories.html.erb index 760903ea0..adac52fc7 100644 --- a/app/views/settings/_repositories.html.erb +++ b/app/views/settings/_repositories.html.erb @@ -52,6 +52,8 @@

<%= setting_text_field :repository_log_display_limit, :size => 6 %>

+ +

<%= setting_check_box :commit_logs_formatting %>

diff --git a/app/views/settings/_timelog.html.erb b/app/views/settings/_timelog.html.erb new file mode 100644 index 000000000..4f922011b --- /dev/null +++ b/app/views/settings/_timelog.html.erb @@ -0,0 +1,10 @@ +<%= form_tag({:action => 'edit', :tab => 'timelog'}) do %> + +
+

<%= setting_multiselect(:timelog_required_fields, + [[l(:field_issue), 'issue_id'], [l(:field_comments), 'comments'] ]) %>

+ +
+ +<%= submit_tag l(:button_save) %> +<% end %> diff --git a/app/views/settings/edit.html.erb b/app/views/settings/edit.html.erb index de64e3bb2..3db255e29 100644 --- a/app/views/settings/edit.html.erb +++ b/app/views/settings/edit.html.erb @@ -1,5 +1,7 @@

<%= l(:label_settings) %>

+<%= render_settings_error @setting_errors %> + <%= render_tabs administration_settings_tabs %> <% html_title(l(:label_settings), l(:label_administration)) -%> diff --git a/app/views/timelog/_date_range.html.erb b/app/views/timelog/_date_range.html.erb index 9e6014653..db07c465e 100644 --- a/app/views/timelog/_date_range.html.erb +++ b/app/views/timelog/_date_range.html.erb @@ -1,36 +1,11 @@ -
-
-
"> - <%= l(:label_filter_plural) %> -
"> - <%= render :partial => 'queries/filters', :locals => {:query => @query} %> -
-
- -
- -

- <%= link_to_function l(:button_apply), '$("#query_form").submit()', :class => 'icon icon-checked' %> - <%= link_to l(:button_clear), {:project_id => @project, :issue_id => @issue}, :class => 'icon icon-reload' %> -

-
+<%= render :partial => 'queries/query_form' %>
-<% query_params = params.slice(:f, :op, :v, :sort) %> +<% query_params = request.query_parameters %>
    -
  • <%= link_to(l(:label_details), _time_entries_path(@project, @issue, query_params), +
  • <%= link_to(l(:label_details), _time_entries_path(@project, nil, :params => query_params), :class => (action_name == 'index' ? 'selected' : nil)) %>
  • -
  • <%= link_to(l(:label_report), _report_time_entries_path(@project, @issue, query_params), +
  • <%= link_to(l(:label_report), _report_time_entries_path(@project, nil, :params => query_params), :class => (action_name == 'report' ? 'selected' : nil)) %>
diff --git a/app/views/timelog/_form.html.erb b/app/views/timelog/_form.html.erb index ebd9d3945..aa62ffc78 100644 --- a/app/views/timelog/_form.html.erb +++ b/app/views/timelog/_form.html.erb @@ -2,24 +2,23 @@ <%= back_url_hidden_field_tag %>
- <% if @time_entry.new_record? %> - <% if params[:project_id] %> - <%= hidden_field_tag 'project_id', params[:project_id] %> - <% elsif params[:issue_id] %> - <%= hidden_field_tag 'issue_id', params[:issue_id] %> - <% else %> -

<%= f.select :project_id, project_tree_options_for_select(Project.allowed_to(:log_time).to_a, :selected => @time_entry.project, :include_blank => true) %>

- <% end %> + <% if @time_entry.new_record? && params[:project_id] %> + <%= hidden_field_tag 'project_id', params[:project_id] %> + <% elsif @time_entry.new_record? && params[:issue_id] %> + <%= hidden_field_tag 'issue_id', params[:issue_id] %> + <% else %> +

<%= f.select :project_id, project_tree_options_for_select(Project.allowed_to(:log_time).to_a, :selected => @time_entry.project, :include_blank => true), :required => true %>

<% end %> +

- <%= f.text_field :issue_id, :size => 6 %> - <% if @time_entry.issue.try(:visible?) %> - <%= "#{@time_entry.issue.tracker.name} ##{@time_entry.issue.id}: #{@time_entry.issue.subject}" %> - <% end %> + <%= f.text_field :issue_id, :size => 6, :required => Setting.timelog_required_fields.include?('issue_id') %> + + <%= link_to_issue(@time_entry.issue) if @time_entry.issue.try(:visible?) %> +

<%= f.date_field :spent_on, :size => 10, :required => true %><%= calendar_for('time_entry_spent_on') %>

-

<%= f.text_field :hours, :size => 6, :required => true %>

-

<%= f.text_field :comments, :size => 100, :maxlength => 1024 %>

+

<%= f.hours_field :hours, :size => 6, :required => true %>

+

<%= f.text_field :comments, :size => 100, :maxlength => 1024, :required => Setting.timelog_required_fields.include?('comments') %>

<%= f.select :activity_id, activity_collection_for_select_options(@time_entry), :required => true %>

<% @time_entry.custom_field_values.each do |value| %>

<%= custom_field_tag_with_label :time_entry, value %>

@@ -28,22 +27,50 @@
<%= javascript_tag do %> -<% if @time_entry.new_record? %> $(document).ready(function(){ + $('#time_entry_project_id').change(function(){ + $('#time_entry_issue_id').val(''); + }); $('#time_entry_project_id, #time_entry_issue_id').change(function(){ $.ajax({ - url: '<%= escape_javascript new_time_entry_path(:format => 'js') %>', + url: '<%= escape_javascript(@time_entry.new_record? ? new_time_entry_path(:format => 'js') : edit_time_entry_path(:format => 'js')) %>', type: 'post', - data: $('#new_time_entry').serialize() + data: $(this).closest('form').serialize() }); }); }); -<% end %> - observeAutocompleteField('time_entry_issue_id', '<%= escape_javascript auto_complete_issues_path(:project_id => @project, :scope => (@project ? nil : 'all'))%>', { - select: function(event, ui) { - $('#time_entry_issue').text(ui.item.label); - $('#time_entry_issue_id').blur(); + observeAutocompleteField('time_entry_issue_id', + function(request, callback) { + var url = '<%= j auto_complete_issues_path %>'; + var data = { + term: request.term + }; + var project_id; + <% if @time_entry.new_record? && @project %> + project_id = '<%= @project.id %>'; + <% else %> + project_id = $('#time_entry_project_id').val(); + <% end %> + if(project_id){ + data['project_id'] = project_id; + } else { + data['scope'] = 'all'; + } + + $.get(url, data, null, 'json') + .done(function(data){ + callback(data); + }) + .fail(function(jqXHR, status, error){ + callback([]); + }); + }, + { + select: function(event, ui) { + $('#time_entry_issue').text(''); + $('#time_entry_issue_id').val(ui.item.value).change(); + } } - }); + ); <% end %> diff --git a/app/views/timelog/_list.html.erb b/app/views/timelog/_list.html.erb index 5ee9eea67..6f126b0f7 100644 --- a/app/views/timelog/_list.html.erb +++ b/app/views/timelog/_list.html.erb @@ -1,7 +1,7 @@ -<%= form_tag({}) do -%> -<%= hidden_field_tag 'back_url', url_for(params) %> +<%= form_tag({}, :data => {:cm_url => time_entries_context_menu_path}) do -%> +<%= hidden_field_tag 'back_url', url_for(:params => request.query_parameters), :id => nil %>
- +
<% @query.inline_columns.each do |column| %> - <%= column_header(column) %> + <%= column_header(@query, column) %> <% end %> -<% entries.each do |entry| -%> - hascontextmenu"> +<% grouped_query_results(entries, @query) do |entry, group_name, group_count, group_totals| -%> + <% if group_name %> + <% reset_cycle %> + + + + <% end %> + hascontextmenu"> <% @query.inline_columns.each do |column| %> <%= content_tag('td', column_content(column, entry), :class => column.css_classes) %> @@ -34,10 +49,22 @@ <% end -%> + <% @query.block_columns.each do |column| + if (text = column_content(column, issue)) && text.present? -%> + + + + <% end -%> + <% end -%> <% end -%>
@@ -9,14 +9,29 @@ :title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}" %>
+   + <%= group_name %> + <% if group_count %> + <%= group_count %> + <% end %> + <%= group_totals %> + <%= link_to_function("#{l(:button_collapse_all)}/#{l(:button_expand_all)}", + "toggleAllRowGroups(this)", :class => 'toggle-all') %> +
<%= check_box_tag("ids[]", entry.id, false, :id => nil) %>
+ <% if query.block_columns.count > 1 %> + <%= column.caption %> + <% end %> + <%= text %> +
<% end -%> -<%= context_menu time_entries_context_menu_path %> +<%= context_menu %> diff --git a/app/views/timelog/_report_criteria.html.erb b/app/views/timelog/_report_criteria.html.erb index c86b219d1..6af122ec4 100644 --- a/app/views/timelog/_report_criteria.html.erb +++ b/app/views/timelog/_report_criteria.html.erb @@ -1,16 +1,16 @@ <% @report.hours.collect {|h| h[criterias[level]].to_s}.uniq.each do |value| %> <% hours_for_value = select_hours(hours, criterias[level], value) -%> <% next if hours_for_value.empty? -%> - + <%= ("" * level).html_safe %> <%= format_criteria_value(@report.available_criteria[criterias[level]], value) %> <%= ("" * (criterias.length - level - 1)).html_safe -%> <% total = 0 -%> <% @report.periods.each do |period| -%> <% sum = sum_hours(select_hours(hours_for_value, @report.columns, period.to_s)); total += sum -%> - <%= html_hours("%.2f" % sum) if sum > 0 %> + <%= html_hours(format_hours(sum)) if sum > 0 %> <% end -%> - <%= html_hours("%.2f" % total) if total > 0 %> + <%= html_hours(format_hours(total)) if total > 0 %> <% if criterias.length > level+1 -%> <%= render(:partial => 'report_criteria', :locals => {:criterias => criterias, :hours => hours_for_value, :level => (level + 1)}) %> diff --git a/app/views/timelog/bulk_edit.html.erb b/app/views/timelog/bulk_edit.html.erb index 640f7d893..96c953151 100644 --- a/app/views/timelog/bulk_edit.html.erb +++ b/app/views/timelog/bulk_edit.html.erb @@ -1,5 +1,21 @@

<%= l(:label_bulk_edit_selected_time_entries) %>

+<% if @unsaved_time_entries.present? %> +
+ + <%= l(:notice_failed_to_save_time_entries, + :count => @unsaved_time_entries.size, + :total => @saved_time_entries.size, + :ids => @unsaved_time_entries.map {|i| "##{i.id}"}.join(', ')) %> + +
    + <% bulk_edit_error_messages(@unsaved_time_entries).each do |message| %> +
  • <%= message %>
  • + <% end %> +
+
+<% end %> +
    <% @time_entries.each do |entry| %> <%= content_tag 'li', @@ -12,29 +28,29 @@

    - + <%= text_field :time_entry, :issue_id, :size => 6 %>

    - + <%= date_field :time_entry, :spent_on, :size => 10 %><%= calendar_for('time_entry_spent_on') %>

    - + <%= text_field :time_entry, :hours, :size => 6 %>

    <% if @available_activities.any? %>

    - + <%= select_tag('time_entry[activity_id]', content_tag('option', l(:label_no_change_option), :value => '') + options_from_collection_for_select(@available_activities, :id, :name)) %>

    <% end %>

    - + <%= text_field(:time_entry, :comments, :size => 100) %>

    diff --git a/app/views/timelog/edit.html.erb b/app/views/timelog/edit.html.erb index de0ea5993..89a60b7ed 100644 --- a/app/views/timelog/edit.html.erb +++ b/app/views/timelog/edit.html.erb @@ -1,6 +1,6 @@

    <%= l(:label_spent_time) %>

    -<%= labelled_form_for @time_entry, :url => time_entry_path(@time_entry) do |f| %> +<%= labelled_form_for @time_entry, :url => time_entry_path(@time_entry), :html => {:multipart => true} do |f| %> <%= render :partial => 'form', :locals => {:f => f} %> <%= submit_tag l(:button_save) %> <% end %> diff --git a/app/views/timelog/edit.js.erb b/app/views/timelog/edit.js.erb new file mode 100644 index 000000000..4cba8cfe6 --- /dev/null +++ b/app/views/timelog/edit.js.erb @@ -0,0 +1,2 @@ +$('#time_entry_activity_id').html('<%= escape_javascript options_for_select(activity_collection_for_select_options(@time_entry), @time_entry.activity_id) %>'); +$('#time_entry_issue').html('<%= escape_javascript link_to_issue(@time_entry.issue) if @time_entry.issue.try(:visible?) %>'); diff --git a/app/views/timelog/index.html.erb b/app/views/timelog/index.html.erb index a875e53af..f777dac30 100644 --- a/app/views/timelog/index.html.erb +++ b/app/views/timelog/index.html.erb @@ -1,37 +1,35 @@
    <%= link_to l(:button_log_time), - _new_time_entry_path(@project, @issue), + _new_time_entry_path(@project, @query.filtered_issue_id), :class => 'icon icon-time-add' if User.current.allowed_to?(:log_time, @project, :global => true) %>
    -<%= render_timelog_breadcrumb %> +

    <%= @query.new_record? ? l(:label_spent_time) : @query.name %>

    -

    <%= l(:label_spent_time) %>

    - -<%= form_tag(params.slice(:project_id, :issue_id), :method => :get, :id => 'query_form') do %> +<%= form_tag(_time_entries_path(@project, nil), :method => :get, :id => 'query_form') do %> <%= render :partial => 'date_range' %> <% end %> -
    -

    <%= l(:label_total_time) %>: <%= html_hours(l_hours(@total_hours)) %>

    -
    - -<% unless @entries.empty? %> +<% if @query.valid? %> +<% if @entries.empty? %> +

    <%= l(:label_no_data) %>

    +<% else %> +<%= render_query_totals(@query) %> <%= render :partial => 'list', :locals => { :entries => @entries }%> <%= pagination_links_full @entry_pages, @entry_count %> <% other_formats_links do |f| %> - <%= f.link_to 'Atom', :url => params.merge({:issue_id => @issue, :key => User.current.rss_key}) %> - <%= f.link_to 'CSV', :url => params, :onclick => "showModal('csv-export-options', '330px'); return false;" %> + <%= f.link_to_with_query_parameters 'Atom', :key => User.current.rss_key %> + <%= f.link_to_with_query_parameters 'CSV', {}, :onclick => "showModal('csv-export-options', '330px'); return false;" %> <% end %> <% end %> +<% end %> -<% html_title l(:label_spent_time), l(:label_details) %> +<% content_for :sidebar do %> + <%= render_sidebar_queries(TimeEntryQuery, @project) %> +<% end %> + +<% html_title(@query.new_record? ? l(:label_spent_time) : @query.name, l(:label_details)) %> <% content_for :header_tags do %> <%= auto_discovery_link_tag(:atom, {:issue_id => @issue, :format => 'atom', :key => User.current.rss_key}, :title => l(:label_spent_time)) %> diff --git a/app/views/timelog/new.html.erb b/app/views/timelog/new.html.erb index 84bf7dae1..593eb2bc8 100644 --- a/app/views/timelog/new.html.erb +++ b/app/views/timelog/new.html.erb @@ -1,6 +1,6 @@

    <%= l(:label_spent_time) %>

    -<%= labelled_form_for @time_entry, :url => time_entries_path do |f| %> +<%= labelled_form_for @time_entry, :url => time_entries_path, :html => {:multipart => true} do |f| %> <%= render :partial => 'form', :locals => {:f => f} %> <%= submit_tag l(:button_create) %> <%= submit_tag l(:button_create_and_continue), :name => 'continue' %> diff --git a/app/views/timelog/new.js.erb b/app/views/timelog/new.js.erb index f76cbb84f..4cba8cfe6 100644 --- a/app/views/timelog/new.js.erb +++ b/app/views/timelog/new.js.erb @@ -1 +1,2 @@ $('#time_entry_activity_id').html('<%= escape_javascript options_for_select(activity_collection_for_select_options(@time_entry), @time_entry.activity_id) %>'); +$('#time_entry_issue').html('<%= escape_javascript link_to_issue(@time_entry.issue) if @time_entry.issue.try(:visible?) %>'); diff --git a/app/views/timelog/report.html.erb b/app/views/timelog/report.html.erb index 7d022aac3..0565c662b 100644 --- a/app/views/timelog/report.html.erb +++ b/app/views/timelog/report.html.erb @@ -4,11 +4,9 @@ :class => 'icon icon-time-add' if User.current.allowed_to?(:log_time, @project, :global => true) %>
    -<%= render_timelog_breadcrumb %> +

    <%= @query.new_record? ? l(:label_spent_time) : @query.name %>

    -

    <%= l(:label_spent_time) %>

    - -<%= form_tag(params.slice(:project_id, :issue_id), :method => :get, :id => 'query_form') do %> +<%= form_tag(_report_time_entries_path(@project, nil), :method => :get, :id => 'query_form') do %> <% @report.criteria.each do |criterion| %> <%= hidden_field_tag 'criteria[]', criterion, :id => nil %> <% end %> @@ -28,12 +26,11 @@ <%= link_to l(:button_clear), {:params => request.query_parameters.merge(:criteria => nil)}, :class => 'icon icon-reload' %>

    <% end %> +<% if @query.valid? %> <% unless @report.criteria.empty? %> -
    -

    <%= l(:label_total_time) %>: <%= html_hours(l_hours(@report.total_hours)) %>

    -
    - -<% unless @report.hours.empty? %> +<% if @report.hours.empty? %> +

    <%= l(:label_no_data) %>

    +<% else %>
    @@ -56,19 +53,24 @@ <% total = 0 -%> <% @report.periods.each do |period| -%> <% sum = sum_hours(select_hours(@report.hours, @report.columns, period.to_s)); total += sum -%> - + <% end -%> - +
    <%= html_hours("%.2f" % sum) if sum > 0 %><%= html_hours(format_hours(sum)) if sum > 0 %><%= html_hours("%.2f" % total) if total > 0 %><%= html_hours(format_hours(total)) if total > 0 %>
    <% other_formats_links do |f| %> - <%= f.link_to 'CSV', :url => params %> + <%= f.link_to_with_query_parameters 'CSV' %> +<% end %> <% end %> <% end %> <% end %> -<% html_title l(:label_spent_time), l(:label_report) %> +<% content_for :sidebar do %> + <%= render_sidebar_queries(TimeEntryQuery, @project) %> +<% end %> + +<% html_title(@query.new_record? ? l(:label_spent_time) : @query.name, l(:label_report)) %> diff --git a/app/views/trackers/fields.html.erb b/app/views/trackers/fields.html.erb index b53f1fa0f..8ba1c3ae4 100644 --- a/app/views/trackers/fields.html.erb +++ b/app/views/trackers/fields.html.erb @@ -25,15 +25,16 @@ <% Tracker::CORE_FIELDS.each do |field| %> - "> + + <% field_name = l("field_#{field}".sub(/_id$/, '')) %> <%= link_to_function('', "toggleCheckboxesBySelector('input.core-field-#{field}')", :title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}", :class => 'icon-only icon-checked') %> - <%= l("field_#{field}".sub(/_id$/, '')) %> + <%= field_name %> <% @trackers.each do |tracker| %> - + "> <%= check_box_tag "trackers[#{tracker.id}][core_fields][]", field, tracker.core_fields.include?(field), :class => "tracker-#{tracker.id} core-field-#{field}", :id => nil %> @@ -48,7 +49,7 @@ <% @custom_fields.each do |field| %> - "> + <%= link_to_function('', "toggleCheckboxesBySelector('input.custom-field-#{field.id}')", :title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}", @@ -56,7 +57,7 @@ <%= field.name %> <% @trackers.each do |tracker| %> - + "> <%= check_box_tag "trackers[#{tracker.id}][custom_field_ids][]", field.id, tracker.custom_fields.include?(field), :class => "tracker-#{tracker.id} custom-field-#{field.id}", :id => nil %> diff --git a/app/views/trackers/index.html.erb b/app/views/trackers/index.html.erb index 5f40cf0ee..d5d463b09 100644 --- a/app/views/trackers/index.html.erb +++ b/app/views/trackers/index.html.erb @@ -13,10 +13,10 @@ <% for tracker in @trackers %> - "> + <%= link_to tracker.name, edit_tracker_path(tracker) %> - <% unless tracker.workflow_rules.count > 0 %> + <% unless tracker.workflow_rules.exists? %> <%= l(:text_tracker_no_workflow) %> (<%= link_to l(:button_edit), workflows_edit_path(:tracker_id => tracker) %>) diff --git a/app/views/users/_general.html.erb b/app/views/users/_general.html.erb index 7d84c0ced..c9398577c 100644 --- a/app/views/users/_general.html.erb +++ b/app/views/users/_general.html.erb @@ -1,7 +1,7 @@ -<%= labelled_form_for @user do |f| %> +<%= labelled_form_for @user, :html => {:multipart => true} do |f| %> <%= render :partial => 'form', :locals => { :f => f } %> <% if @user.active? && email_delivery_enabled? && @user != User.current -%> -

    +

    <% end -%>

    <%= submit_tag l(:button_save) %>

    <% end %> diff --git a/app/views/users/_groups.html.erb b/app/views/users/_groups.html.erb index 204bd9d00..5429f9b31 100644 --- a/app/views/users/_groups.html.erb +++ b/app/views/users/_groups.html.erb @@ -1,9 +1,13 @@ <%= form_for(:user, :url => { :action => 'update' }, :html => {:method => :put}) do %>
    +
    +<% user_group_ids = @user.group_ids %> <% Group.givable.sort.each do |group| %> -
    +
    <% end %> <%= hidden_field_tag 'user[group_ids][]', '' %>
    +

    <%= check_all_links 'user_group_ids' %>

    +
    <%= submit_tag l(:button_save) %> <% end %> diff --git a/app/views/users/_preferences.html.erb b/app/views/users/_preferences.html.erb index fb19bb3e5..f8769125e 100644 --- a/app/views/users/_preferences.html.erb +++ b/app/views/users/_preferences.html.erb @@ -3,4 +3,5 @@

    <%= pref_fields.time_zone_select :time_zone, nil, :include_blank => true %>

    <%= pref_fields.select :comments_sorting, [[l(:label_chronological_order), 'asc'], [l(:label_reverse_chronological_order), 'desc']] %>

    <%= pref_fields.check_box :warn_on_leaving_unsaved %>

    +

    <%= pref_fields.select :textarea_font, textarea_font_options %>

    <% end %> diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb index c32b46a9c..3fc82b640 100644 --- a/app/views/users/index.html.erb +++ b/app/views/users/index.html.erb @@ -22,8 +22,9 @@ <% end %>   +<% if @users.any? %>
    - +
    <%= sort_header_tag('login', :caption => l(:field_login)) %> <%= sort_header_tag('firstname', :caption => l(:field_firstname)) %> @@ -36,7 +37,7 @@ <% for user in @users -%> - "> + @@ -54,5 +55,8 @@
    <%= avatar(user, :size => "14") %><%= link_to user.login, edit_user_path(user) %> <%= user.firstname %> <%= user.lastname %>
    <%= pagination_links_full @user_pages, @user_count %> +<% else %> +

    <%= l(:label_no_data) %>

    +<% end %> <% html_title(l(:label_user_plural)) -%> diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb index 255788e02..088f272e4 100644 --- a/app/views/users/new.html.erb +++ b/app/views/users/new.html.erb @@ -1,9 +1,9 @@ <%= title [l(:label_user_plural), users_path], l(:label_user_new) %> -<%= labelled_form_for @user do |f| %> +<%= labelled_form_for @user, :html => {:multipart => true} do |f| %> <%= render :partial => 'form', :locals => { :f => f } %> <% if email_delivery_enabled? %> -

    +

    <% end %>

    <%= submit_tag l(:button_create) %> diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index 14fa080e0..6d231b86d 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -1,5 +1,5 @@

    -<%= link_to(l(:button_edit), edit_user_path(@user), :class => 'icon icon-edit') if User.current.admin? %> +<%= link_to(l(:button_edit), edit_user_path(@user), :class => 'icon icon-edit') if User.current.admin? && @user.logged? %>

    <%= avatar @user, :size => "50" %> <%= @user.name %>

    diff --git a/app/views/versions/_form.html.erb b/app/views/versions/_form.html.erb index 3d8ecd4da..ade741f26 100644 --- a/app/views/versions/_form.html.erb +++ b/app/views/versions/_form.html.erb @@ -4,10 +4,15 @@

    <%= f.text_field :name, :size => 60, :required => true %>

    <%= f.text_field :description, :size => 60 %>

    -

    <%= f.select :status, Version::VERSION_STATUSES.collect {|s| [l("version_status_#{s}"), s]} %>

    +<% unless @version.new_record? %> +

    <%= f.select :status, Version::VERSION_STATUSES.collect {|s| [l("version_status_#{s}"), s]} %>

    +<% end %>

    <%= f.text_field :wiki_page_title, :label => :label_wiki_page, :size => 60, :disabled => @project.wiki.nil? %>

    <%= f.date_field :effective_date, :size => 10 %><%= calendar_for('version_effective_date') %>

    <%= f.select :sharing, @version.allowed_sharings.collect {|v| [format_version_sharing(v), v]} %>

    +<% if @version.new_record? %> +

    <%= f.check_box :default_project_version, :label => :field_default_version %>

    +<% end %> <% @version.custom_field_values.each do |value| %>

    <%= custom_field_tag_with_label :version, value %>

    diff --git a/app/views/versions/_overview.html.erb b/app/views/versions/_overview.html.erb index 7942e45e7..2effb3180 100644 --- a/app/views/versions/_overview.html.erb +++ b/app/views/versions/_overview.html.erb @@ -17,9 +17,9 @@ <% if version.issues_count > 0 %> <%= progress_bar([version.closed_percent, version.completed_percent], :titles => - ["%s: %0.0f%" % [l(:label_closed_issues_plural), version.closed_percent], - "%s: %0.0f%" % [l(:field_done_ratio), version.completed_percent]], - :legend => ('%0.0f%' % version.completed_percent)) %> + ["%s: %0.0f%%" % [l(:label_closed_issues_plural), version.closed_percent], + "%s: %0.0f%%" % [l(:field_done_ratio), version.completed_percent]], + :legend => ('%0.0f%%' % version.completed_percent)) %>

    <%= link_to(l(:label_x_issues, :count => version.issues_count), version_filtered_issues_path(version, :status_id => '*')) %> diff --git a/app/views/versions/edit.html.erb b/app/views/versions/edit.html.erb index 3c67f09c0..db6bb84e5 100644 --- a/app/views/versions/edit.html.erb +++ b/app/views/versions/edit.html.erb @@ -1,6 +1,6 @@

    <%=l(:label_version)%>

    -<%= labelled_form_for @version do |f| %> +<%= labelled_form_for @version, :html => {:multipart => true} do |f| %> <%= render :partial => 'form', :locals => { :f => f } %> <%= submit_tag l(:button_save) %> <% end %> diff --git a/app/views/versions/index.html.erb b/app/views/versions/index.html.erb index 0f1d0b842..2914a6dbc 100644 --- a/app/views/versions/index.html.erb +++ b/app/views/versions/index.html.erb @@ -17,13 +17,13 @@
    <% end %>
    -

    <%= link_to_version version, :name => version_anchor(version) %>

    +

    <%= link_to_version version, :name => version_anchor(version) %>

    <%= render :partial => 'versions/overview', :locals => {:version => version} %> <%= render(:partial => "wiki/content", :locals => {:content => version.wiki_page.content}) if version.wiki_page %> <% if (issues = @issues_by_version[version]) && issues.size > 0 %> - <%= form_tag({}) do -%> + <%= form_tag({}, :data => {:cm_url => issues_context_menu_path}) do -%> <% issues.each do |issue| -%> @@ -85,13 +85,13 @@ <% if @completed_versions.present? %>

    - <%= link_to_function l(:label_completed_versions), + <%= link_to_function l(:label_completed_versions), '$("#toggle-completed-versions").toggleClass("collapsed"); $("#completed-versions").toggle()', :id => 'toggle-completed-versions', :class => 'collapsible collapsed' %>

    <% end %> @@ -99,4 +99,4 @@ <% html_title(l(:label_roadmap)) %> -<%= context_menu issues_context_menu_path %> +<%= context_menu %> diff --git a/app/views/versions/new.html.erb b/app/views/versions/new.html.erb index c63edade7..b52436dbb 100644 --- a/app/views/versions/new.html.erb +++ b/app/views/versions/new.html.erb @@ -1,6 +1,6 @@

    <%=l(:label_version_new)%>

    -<%= labelled_form_for @version, :url => project_versions_path(@project) do |f| %> +<%= labelled_form_for @version, :url => project_versions_path(@project), :html => {:multipart => true} do |f| %> <%= render :partial => 'versions/form', :locals => { :f => f } %> <%= submit_tag l(:button_create) %> <% end %> diff --git a/app/views/versions/show.html.erb b/app/views/versions/show.html.erb index 27e70aab4..fc22a9ffb 100644 --- a/app/views/versions/show.html.erb +++ b/app/views/versions/show.html.erb @@ -17,12 +17,14 @@ - + <% if User.current.allowed_to_view_all_time_entries?(@project) %> - + <% end %>
    <%= l(:field_estimated_hours) %><%= html_hours(l_hours(@version.estimated_hours)) %><%= link_to html_hours(l_hours(@version.estimated_hours)), + project_issues_path(@version.project, :set_filter => 1, :status_id => '*', :fixed_version_id => @version.id, :c => [:tracker, :status, :subject, :estimated_hours], :t => [:estimated_hours]) %>
    <%= l(:label_spent_time) %><%= html_hours(l_hours(@version.spent_hours)) %><%= link_to html_hours(l_hours(@version.spent_hours)), + project_time_entries_path(@version.project, :set_filter => 1, :"issue.fixed_version_id" => @version.id) %>
    @@ -30,12 +32,12 @@ <% end %>
    -<%= render_issue_status_by(@version, params[:status_by]) if @version.fixed_issues.count > 0 %> +<%= render_issue_status_by(@version, params[:status_by]) if @version.fixed_issues.exists? %>
    <% if @issues.present? %> -<%= form_tag({}) do -%> +<%= form_tag({}, :data => {:cm_url => issues_context_menu_path}) do -%> <%- @issues.each do |issue| -%> @@ -46,7 +48,7 @@ <% end %> <% end %> -<%= context_menu issues_context_menu_path %> +<%= context_menu %> <% end %> diff --git a/app/views/wiki/history.html.erb b/app/views/wiki/history.html.erb index 136923460..e5e72b102 100644 --- a/app/views/wiki/history.html.erb +++ b/app/views/wiki/history.html.erb @@ -19,7 +19,7 @@ <% show_diff = @versions.size > 1 %> <% line_num = 1 %> <% @versions.each do |ver| %> -"> + <%= link_to ver.version, :action => 'show', :id => @page.title, :project_id => @page.project, :version => ver.version %> <%= radio_button_tag('version', ver.version, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('#cbto-#{line_num+1}').prop('checked', true);") if show_diff && (line_num < @versions.size) %> <%= radio_button_tag('version_from', ver.version, (line_num==2), :id => "cbto-#{line_num}") if show_diff && (line_num > 1) %> diff --git a/app/views/wiki/show.html.erb b/app/views/wiki/show.html.erb index 41dd12d10..7e1471645 100644 --- a/app/views/wiki/show.html.erb +++ b/app/views/wiki/show.html.erb @@ -46,24 +46,26 @@ <%= render(:partial => "wiki/content", :locals => {:content => @content}) %> -<%= link_to_attachments @page %> + <% other_formats_links do |f| %> <%= f.link_to 'PDF', :url => {:id => @page.title, :version => params[:version]} %> diff --git a/app/views/wikis/destroy.html.erb b/app/views/wikis/destroy.html.erb index af7dc6c67..5afb676b2 100644 --- a/app/views/wikis/destroy.html.erb +++ b/app/views/wikis/destroy.html.erb @@ -1,10 +1,11 @@

    <%=l(:label_confirmation)%>

    -
    -

    <%= @project.name %>
    <%=l(:text_wiki_destroy_confirmation)%>

    +
    +

    <%= @project.name %>
    <%=l(:text_wiki_destroy_confirmation)%>

    +
    <%= form_tag({:controller => 'wikis', :action => 'destroy', :id => @project}) do %> -<%= hidden_field_tag "confirm", 1 %> -<%= submit_tag l(:button_delete) %> + <%= hidden_field_tag "confirm", 1 %> + <%= submit_tag l(:button_delete) %> + <%= link_to l(:button_cancel), settings_project_path(@project, :tab => 'wiki') %> <% end %> -
    diff --git a/app/views/workflows/_form.html.erb b/app/views/workflows/_form.html.erb index e7c244735..958c1e43c 100644 --- a/app/views/workflows/_form.html.erb +++ b/app/views/workflows/_form.html.erb @@ -24,17 +24,22 @@ <% for old_status in [nil] + @statuses %> <% next if old_status.nil? && name != 'always' %> - "> + <%= link_to_function('', "toggleCheckboxesBySelector('table.transitions-#{name} input[type=checkbox].old-status-#{old_status.try(:id) || 0}')", :title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}", :class => 'icon-only icon-checked') %> - - <%= old_status ? old_status.name : content_tag('em', l(:label_issue_new)) %> + <% if old_status %> + <% old_status_name = old_status.name %> + <%= old_status_name %> + <% else %> + <% old_status_name = l(:label_issue_new) %> + <%= content_tag('em', old_status_name) %> + <% end %> <% for new_status in @statuses -%> <% checked = workflows.detect {|w| w.old_status == old_status && w.new_status == new_status} %> - + <%= transition_tag workflows, old_status, new_status, name %> <% end -%> diff --git a/app/views/workflows/index.html.erb b/app/views/workflows/index.html.erb index 1a0780532..66785a40c 100644 --- a/app/views/workflows/index.html.erb +++ b/app/views/workflows/index.html.erb @@ -17,7 +17,7 @@ <% @trackers.each do |tracker| -%> - + <%= tracker.name %> <% @roles.each do |role| -%> <% count = @workflow_counts[[tracker.id, role.id]] || 0 %> diff --git a/app/views/workflows/permissions.html.erb b/app/views/workflows/permissions.html.erb index 0fb4c8bb6..3a47560c9 100644 --- a/app/views/workflows/permissions.html.erb +++ b/app/views/workflows/permissions.html.erb @@ -60,12 +60,12 @@ <% @fields.each do |field, name| %> - "> + <%= name %> <%= content_tag('span', '*', :class => 'required') if field_required?(field) %> <% for status in @statuses -%> - + <%= field_permission_tag(@permissions, status, field, @roles) %> <% unless status == @statuses.last %>»<% end %> @@ -80,12 +80,12 @@ <% @custom_fields.each do |field| %> - "> + <%= field.name %> <%= content_tag('span', '*', :class => 'required') if field_required?(field) %> <% for status in @statuses -%> - + <%= field_permission_tag(@permissions, status, field, @roles) %> <% unless status == @statuses.last %>»<% end %> diff --git a/appveyor.yml b/appveyor.yml index b0ac65c5b..d5ea687bb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -8,6 +8,7 @@ install: - gem --version - git --version - hg --version + - chcp build: off diff --git a/config/application.rb b/config/application.rb index 965edcec2..e1a416ae7 100644 --- a/config/application.rb +++ b/config/application.rb @@ -47,7 +47,7 @@ module RedmineApp # Do not include all helpers config.action_controller.include_all_helpers = false - # Do not supress errors in after_rollback and after_commit callbacks + # Do not suppress errors in after_rollback and after_commit callbacks config.active_record.raise_in_transactional_callbacks = true # XML parameter parser removed from core in Rails 4.0 diff --git a/config/initializers/10-patches.rb b/config/initializers/10-patches.rb index ef8b930ef..2693f83f4 100644 --- a/config/initializers/10-patches.rb +++ b/config/initializers/10-patches.rb @@ -70,35 +70,35 @@ module ActionView module Tags class Base private - def add_options_with_non_empty_blank_option(option_tags, options, value = nil) + alias :add_options_without_non_empty_blank_option :add_options + def add_options(option_tags, options, value = nil) if options[:include_blank] == true options = options.dup options[:include_blank] = ' '.html_safe end add_options_without_non_empty_blank_option(option_tags, options, value) end - alias_method_chain :add_options, :non_empty_blank_option end end module FormTagHelper - def select_tag_with_non_empty_blank_option(name, option_tags = nil, options = {}) + alias :select_tag_without_non_empty_blank_option :select_tag + def select_tag(name, option_tags = nil, options = {}) if options.delete(:include_blank) options[:prompt] = ' '.html_safe end select_tag_without_non_empty_blank_option(name, option_tags, options) end - alias_method_chain :select_tag, :non_empty_blank_option end module FormOptionsHelper - def options_for_select_with_non_empty_blank_option(container, selected = nil) + alias :options_for_select_without_non_empty_blank_option :options_for_select + def options_for_select(container, selected = nil) if container.is_a?(Array) container = container.map {|element| element.blank? ? [" ".html_safe, ""] : element} end options_for_select_without_non_empty_blank_option(container, selected) end - alias_method_chain :options_for_select, :non_empty_blank_option end end end @@ -187,3 +187,54 @@ module ActionController end end end + +# Adds asset_id parameters to assets like Rails 3 to invalidate caches in browser +module ActionView + module Helpers + module AssetUrlHelper + @@cache_asset_timestamps = Rails.env.production? + @@asset_timestamps_cache = {} + @@asset_timestamps_cache_guard = Mutex.new + + def asset_path_with_asset_id(source, options = {}) + asset_id = rails_asset_id(source, options) + unless asset_id.blank? + source += "?#{asset_id}" + end + asset_path(source, options) + end + alias :path_to_asset :asset_path_with_asset_id + + def rails_asset_id(source, options = {}) + if asset_id = ENV["RAILS_ASSET_ID"] + asset_id + else + if @@cache_asset_timestamps && (asset_id = @@asset_timestamps_cache[source]) + asset_id + else + extname = compute_asset_extname(source, options) + path = File.join(Rails.public_path, "#{source}#{extname}") + exist = false + if File.exist? path + exist = true + else + path = File.join(Rails.public_path, compute_asset_path("#{source}#{extname}", options)) + if File.exist? path + exist = true + end + end + asset_id = exist ? File.mtime(path).to_i.to_s : '' + + if @@cache_asset_timestamps + @@asset_timestamps_cache_guard.synchronize do + @@asset_timestamps_cache[source] = asset_id + end + end + + asset_id + end + end + end + end + end +end \ No newline at end of file diff --git a/config/locales/ar.yml b/config/locales/ar.yml index 509d18569..6b31412dc 100644 --- a/config/locales/ar.yml +++ b/config/locales/ar.yml @@ -129,6 +129,8 @@ ar: circular_dependency: "هذه العلاقة سوف تخلق علاقة تبعية دائرية" cant_link_an_issue_with_a_descendant: "لا يمكن ان تكون المشكلة مرتبطة بواحدة من المهام الفرعية" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" actionview_instancetag_blank_option: الرجاء التحديد @@ -510,7 +512,6 @@ ar: label_my_page: الصفحة الخاصة بي label_my_account: حسابي label_my_projects: مشاريعي الخاصة - label_my_page_block: حجب صفحتي الخاصة label_administration: إدارة النظام label_login: تسجيل الدخول label_logout: تسجيل الخروج @@ -599,7 +600,6 @@ ar: label_internal: الداخلية label_last_changes: "آخر التغييرات %{count}" label_change_view_all: عرض كافة التغييرات - label_personalize_page: تخصيص هذه الصفحة label_comment: تعليق label_comment_plural: تعليقات label_x_comments: @@ -755,8 +755,6 @@ ar: label_user_mail_option_selected: "الخيارات المظللة فقط" label_user_mail_option_none: "لم يتم تحديد اي خيارات" label_user_mail_option_only_my_events: "السماح لي فقط بمشاهدة الاحداث الخاصة" - label_user_mail_option_only_assigned: "فقط الخيارات التي تم تعيينها" - label_user_mail_option_only_owner: "فقط للخيارات التي املكها" label_user_mail_no_self_notified: "لا تريد إعلامك بالتغيرات التي تجريها بنفسك" label_registration_activation_by_email: حساب التنشيط عبر البريد الإلكتروني label_registration_manual_activation: تنشيط الحساب اليدوي @@ -765,7 +763,6 @@ ar: label_age: العمر label_change_properties: تغيير الخصائص label_general: عامة - label_more: أكثر label_scm: scm label_plugins: الإضافات label_ldap_authentication: مصادقة LDAP @@ -775,7 +772,6 @@ ar: label_preferences: تفضيلات label_chronological_order: في ترتيب زمني label_reverse_chronological_order: في ترتيب زمني عكسي - label_planning: التخطيط label_incoming_emails: رسائل البريد الإلكتروني الوارد label_generate_key: إنشاء مفتاح label_issue_watchers: المراقبون @@ -984,10 +980,6 @@ ar: description_all_columns: كل الاعمدة description_issue_category_reassign: اختر التصنيف description_wiki_subpages_reassign: اختر صفحة جديدة - description_date_range_list: اختر المجال من القائمة - description_date_range_interval: اختر المدة عن طريق اختيار تاريخ البداية والنهاية - description_date_from: ادخل تاريخ البداية - description_date_to: ادخل تاريخ الانتهاء text_rmagick_available: RMagick available (optional) text_wiki_page_destroy_question: This page has %{descendants} child page(s) and descendant(s). What do you want to do? text_repository_usernames_mapping: |- @@ -1208,5 +1200,31 @@ ar: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/az.yml b/config/locales/az.yml index 314d126e6..2d754bab4 100644 --- a/config/locales/az.yml +++ b/config/locales/az.yml @@ -197,6 +197,8 @@ az: circular_dependency: "Belə əlaqə dövri asılılığa gətirib çıxaracaq" cant_link_an_issue_with_a_descendant: "Tapşırıq özünün alt tapşırığı ilə əlaqəli ola bilməz" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" support: array: @@ -563,10 +565,8 @@ az: label_months_from: ay label_month: Ay label_more_than_ago: gündən əvvəl - label_more: Çox label_my_account: Mənim hesabım label_my_page: Mənim səhifəm - label_my_page_block: Mənim səhifəmin bloku label_my_projects: Mənim layihələrim label_new: Yeni label_new_statuses_allowed: İcazə verilən yeni statuslar @@ -594,8 +594,6 @@ az: label_password_lost: Parolun bərpası label_permissions_report: Giriş hüquqları üzrə hesabat label_permissions: Giriş hüquqları - label_personalize_page: bu səhifəni fərdiləşdirmək - label_planning: Planlaşdırma label_please_login: Xahiş edirik, daxil olun. label_plugins: Modullar label_precedes: növbəti @@ -684,9 +682,7 @@ az: label_user_mail_no_self_notified: "Tərəfimdən edilən dəyişikliklər haqqında məni xəbərdar etməmək" label_user_mail_option_all: "Mənim layihələrimdəki bütün hadisələr haqqında" label_user_mail_option_selected: "Yalnız seçilən layihədəki bütün hadisələr haqqında..." - label_user_mail_option_only_owner: Yalnız sahibi olduğum obyektlər üçün label_user_mail_option_only_my_events: Yalnız izlədiyim və ya iştirak etdiyim obyektlər üçün - label_user_mail_option_only_assigned: Yalnız mənə təyin edilən obyektlər üçün label_user_new: Yeni istifadəçi label_user_plural: İstifadəçilər label_version: Variant @@ -1066,16 +1062,12 @@ az: description_project_scope: Layihənin həcmi description_filter: Filtr description_user_mail_notification: E-poçt Mail xəbərdarlıqlarının sazlaması - description_date_from: Başlama tarixini daxil edin description_message_content: Mesajın kontenti description_available_columns: Mövcud sütunlar - description_date_range_interval: Tarixlər diapazonunu seçin description_issue_category_reassign: Məsələnin kateqoriyasını seçin description_search: Axtarış sahəsi description_notes: Qeyd - description_date_range_list: Siyahıdan diapazonu seçin description_choose_project: Layihələr - description_date_to: Yerinə yetirilmə tarixini daxil edin description_query_sort_criteria_attribute: Çeşidləmə meyarları description_wiki_subpages_reassign: Yeni valideyn səhifəsini seçmək description_selected_columns: Seçilmiş sütunlar @@ -1303,5 +1295,31 @@ az: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/bg.yml b/config/locales/bg.yml index 1022803bb..2f57ea198 100644 --- a/config/locales/bg.yml +++ b/config/locales/bg.yml @@ -131,6 +131,8 @@ bg: circular_dependency: "Тази релация ще доведе до безкрайна зависимост" cant_link_an_issue_with_a_descendant: "Една задача не може да бъде свързвана към своя подзадача" earlier_than_minimum_start_date: "не може да бъде по-рано от %{date} поради предхождащи задачи" + not_a_regexp: "не е валиден регулярен израз" + open_issue_with_closed_parent: "Отворена задача не може да бъде асоциирана към затворена родителска задача" actionview_instancetag_blank_option: Изберете @@ -217,7 +219,9 @@ bg: error_ldap_bind_credentials: Невалидни LDAP име/парола error_no_tracker_allowed_for_new_issue_in_project: Проектът няма тракери, за които да създавате задачи error_no_projects_with_tracker_allowed_for_new_issue: Няма проекти с тракери за които можете да създавате задачи + error_move_of_child_not_possible: 'Подзадача %{child} не беше преместена в новия проект: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Употребеното време не може да бъде прехвърлено на задача, която ще бъде изтрита + warning_fields_cleared_on_bulk_edit: Промените ще предизвикат автоматично изтриване на стойности от едно или повече полета на избраните обекти mail_subject_lost_password: "Вашата парола (%{value})" mail_body_lost_password: 'За да смените паролата си, използвайте следния линк:' @@ -367,6 +371,12 @@ bg: field_total_estimated_hours: Общо изчислено време field_default_version: Версия по подразбиране field_remote_ip: IP адрес + field_textarea_font: Шрифт за текстови блокове + field_updated_by: Обновено от + field_last_updated_by: Последно обновено от + field_full_width_layout: Пълна широчина + field_digest: Контролна сума + field_default_assigned_to: Назначение по подразбиране setting_app_title: Заглавие setting_app_subtitle: Описание @@ -374,6 +384,7 @@ bg: setting_default_language: Език по подразбиране setting_login_required: Изискване за вход в Redmine setting_self_registration: Регистрация от потребители + setting_show_custom_fields_on_registration: Показване на потребителските полета при регистрацията setting_attachment_max_size: Максимална големина на прикачен файл setting_issues_export_limit: Максимален брой задачи за експорт setting_mail_from: E-mail адрес за емисии @@ -391,6 +402,7 @@ bg: setting_autologin: Автоматичен вход setting_date_format: Формат на датата setting_time_format: Формат на часа + setting_timespan_format: Формат на интервал от време setting_cross_project_issue_relations: Релации на задачи между проекти setting_cross_project_subtasks: Подзадачи от други проекти setting_issue_list_default_columns: Показвани колони по подразбиране @@ -404,6 +416,7 @@ bg: setting_display_subprojects_issues: Задачите от подпроектите по подразбиране се показват в главните проекти setting_enabled_scm: Разрешена SCM setting_mail_handler_body_delimiters: Отрязване на e-mail-ите след един от тези редове + setting_mail_handler_enable_regex_delimiters: Разрешаванен на регулярни изрази setting_mail_handler_api_enabled: Разрешаване на WS за входящи e-mail-и setting_mail_handler_api_key: API ключ за входящи e-mail-и setting_sys_api_key: API ключ за хранилища @@ -449,6 +462,8 @@ bg: setting_attachment_extensions_allowed: Позволени типове на файлове setting_attachment_extensions_denied: Разрешени типове на файлове setting_new_item_menu_tab: Меню-елемент за добавяне на нови обекти (+) + setting_commit_logs_formatting: Прилагане на форматиране за съобщенията при поверяване + setting_timelog_required_fields: Задължителни полета за записи за изразходваното време permission_add_project: Създаване на проект permission_add_subprojects: Създаване на подпроекти @@ -484,6 +499,7 @@ bg: permission_view_time_entries: Разглеждане на записите за изразходваното време permission_edit_time_entries: Редактиране на записите за изразходваното време permission_edit_own_time_entries: Редактиране на собствените записи за изразходваното време + permission_view_news: Разглеждане на новини permission_manage_news: Управление на новини permission_comment_news: Коментиране на новини permission_view_documents: Разглеждане на документи @@ -591,7 +607,6 @@ bg: label_my_page: Лична страница label_my_account: Профил label_my_projects: Проекти, в които участвам - label_my_page_block: Блокове в личната страница label_administration: Администрация label_login: Вход label_logout: Изход @@ -626,6 +641,8 @@ bg: label_attribute_plural: Атрибути label_no_data: Няма изходни данни label_no_preview: Няма наличен преглед (preview) + label_no_preview_alternative_html: Няма наличен преглед (preview). %{link} файл вместо това. + label_no_preview_download: Изтегляне label_change_status: Промяна на състоянието label_history: История label_attachment: Файл @@ -689,7 +706,6 @@ bg: label_internal: Вътрешен label_last_changes: "последни %{count} промени" label_change_view_all: Виж всички промени - label_personalize_page: Персонализиране label_comment: Коментар label_comment_plural: Коментари label_x_comments: @@ -859,8 +875,8 @@ bg: label_user_mail_option_selected: "За всички събития само в избраните проекти..." label_user_mail_option_none: "Само за наблюдавани или в които участвам (автор или назначени на мен)" label_user_mail_option_only_my_events: Само за неща, в които съм включен/а - label_user_mail_option_only_assigned: Само за неща, назначени на мен - label_user_mail_option_only_owner: Само за неща, на които аз съм собственик + label_user_mail_option_only_assigned: Само за неща, които наблюдавам или са назначени на мен + label_user_mail_option_only_owner: Само за неща, които наблюдавам или съм техен собственик label_user_mail_no_self_notified: "Не искам известия за извършени от мен промени" label_registration_activation_by_email: активиране на профила по email label_registration_manual_activation: ръчно активиране @@ -869,7 +885,6 @@ bg: label_age: Възраст label_change_properties: Промяна на настройки label_general: Основни - label_more: Още label_scm: SCM (Система за контрол на версиите) label_plugins: Плъгини label_ldap_authentication: LDAP оторизация @@ -879,7 +894,6 @@ bg: label_preferences: Предпочитания label_chronological_order: Хронологичен ред label_reverse_chronological_order: Обратен хронологичен ред - label_planning: Планиране label_incoming_emails: Входящи e-mail-и label_generate_key: Генериране на ключ label_issue_watchers: Наблюдатели @@ -942,6 +956,7 @@ bg: label_attribute_of_assigned_to: Assignee's %{name} label_attribute_of_user: User's %{name} label_attribute_of_fixed_version: Target version's %{name} + label_attribute_of_object: "%{name} на %{object_name}" label_cross_project_descendants: С подпроекти label_cross_project_tree: С дърво на проектите label_cross_project_hierarchy: С проектна йерархия @@ -1001,6 +1016,11 @@ bg: label_relations: Релации label_new_project_issue_tab_enabled: Показване на меню-елемент "Нова задача" label_new_object_tab_enabled: Показване на изпадащ списък за меню-елемент "+" + label_table_of_contents: Съдържание + label_font_default: Шрифт по подразбиране + label_font_monospace: Monospaced шрифт + label_font_proportional: Пропорционален шрифт + label_last_notes: Последни коментари button_login: Вход button_submit: Изпращане @@ -1191,8 +1211,4 @@ bg: description_issue_category_reassign: Изберете категория description_wiki_subpages_reassign: Изберете нова родителска страница description_all_columns: Всички колони - description_date_range_list: Изберете диапазон от списъка - description_date_range_interval: Изберете диапазон чрез задаване на начална и крайна дати - description_date_from: Въведете начална дата - description_date_to: Въведете крайна дата text_repository_identifier_info: 'Позволени са малки букви (a-z), цифри, тирета и _.
    Промяна след създаването му не е възможна.' diff --git a/config/locales/bs.yml b/config/locales/bs.yml index 900255d7e..1c51ecffc 100644 --- a/config/locales/bs.yml +++ b/config/locales/bs.yml @@ -141,6 +141,8 @@ bs: circular_dependency: "Ova relacija stvar cirkularnu zavisnost" cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" actionview_instancetag_blank_option: Molimo odaberite @@ -530,7 +532,6 @@ bs: label_internal: Interno label_last_changes: "posljednjih %{count} promjena" label_change_view_all: Vidi sve promjene - label_personalize_page: Personaliziraj ovu stranicu label_comment: Komentar label_comment_plural: Komentari label_x_comments: @@ -677,7 +678,6 @@ bs: label_age: Starost label_change_properties: Promjena osobina label_general: Generalno - label_more: Više label_scm: SCM label_plugins: Plugin-ovi label_ldap_authentication: LDAP authentifikacija @@ -687,7 +687,6 @@ bs: label_preferences: Postavke label_chronological_order: Hronološki poredak label_reverse_chronological_order: Reverzni hronološki poredak - label_planning: Planiranje label_incoming_emails: Dolazni email-ovi label_generate_key: Generiši ključ label_issue_watchers: Praćeno od @@ -909,7 +908,6 @@ bs: error_can_not_remove_role: This role is in use and can not be deleted. error_can_not_delete_tracker: This tracker contains issues and cannot be deleted. field_principal: Principal - label_my_page_block: My page block notice_failed_to_save_members: "Failed to save member(s): %{errors}." text_zoom_out: Zoom out text_zoom_in: Zoom in @@ -920,10 +918,8 @@ bs: project_module_calendar: Calendar button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}" field_text: Text field - label_user_mail_option_only_owner: Only for things I am the owner of setting_default_notification_option: Default notification option label_user_mail_option_only_my_events: Only for things I watch or I'm involved in - label_user_mail_option_only_assigned: Only for things I am assigned to label_user_mail_option_none: No events field_member_of_group: Assignee's group field_assigned_to_role: Assignee's role @@ -980,16 +976,12 @@ bs: description_project_scope: Search scope description_filter: Filter description_user_mail_notification: Mail notification settings - description_date_from: Enter start date description_message_content: Message content description_available_columns: Available Columns - description_date_range_interval: Choose range by selecting start and end date description_issue_category_reassign: Choose issue category description_search: Searchfield description_notes: Notes - description_date_range_list: Choose range from list description_choose_project: Projects - description_date_to: Enter end date description_query_sort_criteria_attribute: Sort attribute description_wiki_subpages_reassign: Choose new parent page description_selected_columns: Selected Columns @@ -1221,5 +1213,31 @@ bs: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/ca.yml b/config/locales/ca.yml index 32186d276..9d9bc73a3 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -134,6 +134,8 @@ ca: circular_dependency: "Aquesta relació crearia una dependència circular" cant_link_an_issue_with_a_descendant: "Un assumpte no es pot enllaçar a una de les seves subtasques" earlier_than_minimum_start_date: "no pot ser anterior a %{date} derivat a les peticions precedents" + 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" actionview_instancetag_blank_option: "Seleccionar" @@ -221,7 +223,7 @@ ca: field_lastname: "Cognom" field_mail: "Correu electrònic" field_filename: "Fitxer" - field_filesize: "Tamany" + field_filesize: "Mida" field_downloads: "Baixades" field_author: "Autor" field_created_on: "Creat" @@ -481,7 +483,6 @@ ca: label_my_page: "La meva pàgina" label_my_account: "El meu compte" label_my_projects: "Els meus projectes" - label_my_page_block: "Els meus blocs de pàgina" label_administration: "Administració" label_login: "Iniciar sessió" label_logout: "Tancar sessió" @@ -503,7 +504,7 @@ ca: label_subproject_plural: "Subprojectes" label_subproject_new: "Nou subprojecte" label_and_its_subprojects: "%{value} i els seus subprojectes" - label_min_max_length: "Longitud mín - max" + label_min_max_length: "Longitud mín - màx" label_list: "Llista" label_date: "Data" label_integer: "Numero" @@ -569,7 +570,6 @@ ca: label_internal: "Intern" label_last_changes: "últims %{count} canvis" label_change_view_all: "Visualitza tots els canvis" - label_personalize_page: "Personalitza aquesta pàgina" label_comment: "Comentari" label_comment_plural: "Comentaris" label_x_comments: @@ -727,7 +727,6 @@ ca: label_age: "Edat" label_change_properties: "Canvia les propietats" label_general: "General" - label_more: "Més" label_scm: "SCM" label_plugins: "Complements" label_ldap_authentication: "Autenticació LDAP" @@ -737,7 +736,6 @@ ca: label_preferences: "Preferències" label_chronological_order: "En ordre cronològic" label_reverse_chronological_order: "En ordre cronològic invers" - label_planning: "Planificació" label_incoming_emails: "Correu electrònics d'entrada" label_generate_key: "Generar una clau" label_issue_watchers: "Vigilàncies" @@ -910,14 +908,12 @@ ca: button_edit_associated_wikipage: "Editar pàgines Wiki asociades: %{page_title}" field_text: "Camp de text" - label_user_mail_option_only_owner: "Només pels objectes creats per mi" setting_default_notification_option: "Opció de notificació per defecte" label_user_mail_option_only_my_events: "Només pels objectes on estic en vigilància o involucrat" - label_user_mail_option_only_assigned: "Només pels objectes on estic assignat" label_user_mail_option_none: "Sense notificacions" field_member_of_group: "Assignat al grup" field_assigned_to_role: "Assignat al rol" - notice_not_authorized_archived_project: "El projecte al que intenta accedir esta arxivat." + notice_not_authorized_archived_project: "El projecte al que intenta accedir està arxivat." label_principal_search: "Cercar per usuari o grup:" label_user_search: "Cercar per usuari:" field_visible: "Visible" @@ -936,7 +932,7 @@ ca: label_additional_workflow_transitions_for_assignee: "Operacions addicionals permeses quan l'usuari té assignat l'assumpte" label_additional_workflow_transitions_for_author: "Operacions addicionals permeses quan l'usuari és propietari de l'assumpte" label_bulk_edit_selected_time_entries: "Editar en bloc els registres de temps seleccionats" - text_time_entries_destroy_confirmation: "Esta segur de voler eliminar (l'hora seleccionada/les hores seleccionades)?" + text_time_entries_destroy_confirmation: "Està segur de voler eliminar (l'hora seleccionada/les hores seleccionades)?" label_role_anonymous: "Anònim" label_role_non_member: "No membre" label_issue_note_added: "Nota afegida" @@ -970,29 +966,25 @@ ca: description_project_scope: "Àmbit de la cerca" description_filter: "Filtre" description_user_mail_notification: "Configuració de les notificacions per correu" - description_date_from: "Introduir data d'inici" description_message_content: "Contingut del missatge" description_available_columns: "Columnes disponibles" - description_date_range_interval: "Escollir un rang seleccionat de dates (inici i fi)" description_issue_category_reassign: "Escollir una categoria de l'assumpte" description_search: "Camp de cerca" description_notes: "Notes" - description_date_range_list: "Escollir un rang de la llista" description_choose_project: "Projectes" - description_date_to: "Introduir data final" description_query_sort_criteria_attribute: "Atribut d'ordenació" - description_wiki_subpages_reassign: "Esculli la nova pagina pare" + description_wiki_subpages_reassign: "Esculli la nova pàgina pare" description_selected_columns: "Columnes seleccionades" label_parent_revision: "Pare" label_child_revision: "Fill" - error_scm_annotate_big_text_file: "L'entrada no es pot anotar, ja que supera el tamany màxim per fitxers de text." + error_scm_annotate_big_text_file: "L'entrada no es pot anotar, ja que supera la mida màxima per fitxers de text." setting_default_issue_start_date_to_creation_date: "Utilitzar la data actual com a data inici per les noves peticions" button_edit_section: "Editar aquest apartat" setting_repositories_encodings: "Codificació per defecte pels fitxers adjunts i repositoris" description_all_columns: "Totes les columnes" button_export: "Exportar" label_export_options: "%{export_format} opcions d'exportació" - error_attachment_too_big: "Aquest fitxer no es pot pujar perquè excedeix del tamany màxim (%{max_size})" + error_attachment_too_big: "Aquest fitxer no es pot pujar perquè excedeix de la mida màxima (%{max_size})" notice_failed_to_save_time_entries: "Error al desar %{count} entrades de temps de les %{total} selecionades: %{ids}." label_x_issues: zero: "0 assumpte" @@ -1004,7 +996,7 @@ ca: label_item_position: "%{position}/%{count}" label_completed_versions: "Versions completades" text_project_identifier_info: "Només es permeten lletres en minúscula (a-z), números i guions.
    Una vegada desat, l'identificador no es pot canviar." - field_multiple: "Valors multiples" + field_multiple: "Valors múltiples" setting_commit_cross_project_ref: "Permetre referenciar i resoldre peticions de tots els altres projectes" text_issue_conflict_resolution_add_notes: "Afegir les meves notes i descartar els altres canvis" text_issue_conflict_resolution_overwrite: "Aplicar els meus canvis de totes formes (les notes anteriors es mantindran però alguns canvis poden ser sobreescrits)" @@ -1017,7 +1009,7 @@ ca: setting_unsubscribe: "Permetre als usuaris d'esborrar el seu propi compte" button_delete_my_account: "Eliminar el meu compte" text_account_destroy_confirmation: |- - Estas segur de continuar? + Estàs segur de continuar? El seu compte s'eliminarà de forma permanent, sense la possibilitat de reactivar-lo. error_session_expired: "La seva sessió ha expirat. Si us plau, torni a identificar-se" text_session_expiration_settings: "Advertència: el canvi d'aquestes opcions poden provocar la expiració de les sessions actives, incloent la seva." @@ -1031,12 +1023,12 @@ ca: project_status_active: "actiu" project_status_closed: "tancat" project_status_archived: "arxivat" - text_project_closed: "Aquest projecte esta tancat i nomes es de lectura." + text_project_closed: "Aquest projecte està tancat i només és de lectura." notice_user_successful_create: "Usuari %{id} creat correctament." field_core_fields: "Camps bàsics" field_timeout: "Temps d'inactivitat (en segons)" setting_thumbnails_enabled: "Mostrar miniatures dels fitxers adjunts" - setting_thumbnails_size: "Tamany de les miniatures (en píxels)" + setting_thumbnails_size: "Mida de les miniatures (en píxels)" label_status_transitions: "Transicions d'estat" label_fields_permissions: "Permisos sobre els camps" label_readonly: "Només lectura" @@ -1049,7 +1041,7 @@ ca: label_attribute_of_fixed_version: "%{name} de la versió objectiu" label_copy_subtasks: "Copiar subtasques" label_copied_to: "copiada a" - label_copied_from: "copiada desde" + label_copied_from: "copiada des de" label_any_issues_in_project: "qualsevol assumpte del projecte" label_any_issues_not_in_project: "qualsevol assumpte que no sigui del projecte" field_private_notes: "Notes privades" @@ -1066,7 +1058,7 @@ ca: button_hide: "Amagar" setting_non_working_week_days: "Dies no laborables" label_in_the_next_days: "en els pròxims" - label_in_the_past_days: "en els anterios" + label_in_the_past_days: "en els anteriors" label_attribute_of_user: "%{name} de l'usuari" text_turning_multiple_off: "Si es desactiva els valors múltiples, aquest seran eliminats per deixar només un únic valor per element." label_attribute_of_issue: "%{name} de l'assumpte" @@ -1075,7 +1067,7 @@ ca: permission_delete_documents: "Eliminar document" label_gantt_progress_line: "Línia de progres" setting_jsonp_enabled: "Habilitar suport JSONP" - field_inherit_members: "Heredar membres" + field_inherit_members: "Heretar membres" field_closed_on: "Tancada" field_generate_password: "Generar contrasenya" setting_default_projects_tracker_ids: "Tipus d'estats d'assumpte habilitat per defecte" @@ -1084,7 +1076,7 @@ ca: text_scm_command_not_available: "L'ordre SCM que es vol utilitzar no és troba disponible. Si us plau, comprovi la configuració dins del menú d'administració" setting_emails_header: "Encapçalament dels correus" notice_account_not_activated_yet: Encara no ha activat el seu compte. Si vol rebre un nou correu d'activació, si us plau faci clic en aquest enllaç. - notice_account_locked: "Aquest compte esta bloquejat." + notice_account_locked: "Aquest compte està bloquejat." label_hidden: "Amagada" label_visibility_private: "només per mi" label_visibility_roles: "només per aquests rols" @@ -1093,7 +1085,7 @@ ca: notice_new_password_must_be_different: "La nova contrasenya ha de ser diferent de l'actual." setting_mail_handler_excluded_filenames: "Excloure fitxers adjunts per nom" text_convert_available: "Conversió ImageMagick disponible (opcional)" - label_link: "Link" + label_link: "Enllaç" label_only: "només" label_drop_down_list: "Llista desplegable (Drop down)" label_checkboxes: "Camps de selecció (Checkboxes)" @@ -1159,7 +1151,7 @@ ca: label_semi_colon_char: "Punt i coma" label_quote_char: "Comilla simple" label_double_quote_char: "Comilla doble" - label_fields_mapping: "Mapeig de camps" + label_fields_mapping: "Mapat de camps" label_file_content_preview: "Vista prèvia del contingut" label_create_missing_values: "Crear valors no presents" button_import: "Importar" @@ -1169,8 +1161,8 @@ ca: label_assigned_issues: "Assumptes assignats" label_field_format_enumeration: "Llistat clau/valor" label_f_hour_short: "%{value} h" - field_default_version: "Versio per defecte" - error_attachment_extension_not_allowed: "L'extensio %{extension} no esta permesa" + field_default_version: "Versió per defecte" + error_attachment_extension_not_allowed: "L'extensió %{extension} no està permesa" setting_attachment_extensions_allowed: "Extensions permeses" setting_attachment_extensions_denied: "Extensions no permeses" label_any_open_issues: "qualsevol assumpte obert" @@ -1198,5 +1190,31 @@ ca: setting_new_item_menu_tab: Pestanya de nous objectes en el menu de cada projecte label_new_object_tab_enabled: Mostrar el llistat desplegable "+" error_no_projects_with_tracker_allowed_for_new_issue: "Cap projecte disposa d'un tipus d'assumpte sobre el qual vostè pugui crear un assumpte" + field_textarea_font: Font utilitzada en les text àrea + label_font_default: Font per defecte + label_font_monospace: Font Monospaced + label_font_proportional: Font Proportional + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/cs.yml b/config/locales/cs.yml index 83e8113b9..819b95814 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -135,6 +135,8 @@ cs: circular_dependency: "Tento vztah by vytvořil cyklickou závislost" cant_link_an_issue_with_a_descendant: "Úkol nemůže být spojen s jedním z jeho dílčích úkolů" earlier_than_minimum_start_date: "nemůže být dříve než %{date} kvůli předřazeným úkolům" + not_a_regexp: "není platný regulární výraz" + open_issue_with_closed_parent: "Otevřený úkol nemůže být přiřazen pod uzavřený rodičovský úkol" actionview_instancetag_blank_option: Prosím vyberte @@ -493,7 +495,6 @@ cs: label_my_page: Moje stránka label_my_account: Můj účet label_my_projects: Moje projekty - label_my_page_block: Bloky na mé stránce label_administration: Administrace label_login: Přihlášení label_logout: Odhlášení @@ -581,7 +582,6 @@ cs: label_internal: Interní label_last_changes: "posledních %{count} změn" label_change_view_all: Zobrazit všechny změny - label_personalize_page: Přizpůsobit tuto stránku label_comment: Komentář label_comment_plural: Komentáře label_x_comments: @@ -733,8 +733,6 @@ cs: label_user_mail_option_selected: "Pro všechny události vybraných projektů..." label_user_mail_option_none: "Žádné události" label_user_mail_option_only_my_events: "Jen pro věci, co sleduji nebo jsem v nich zapojen" - label_user_mail_option_only_assigned: "Jen pro věci, ke kterým sem přiřazen" - label_user_mail_option_only_owner: "Jen pro věci, které vlastním" label_user_mail_no_self_notified: "Nezasílat informace o mnou vytvořených změnách" label_registration_activation_by_email: aktivace účtu emailem label_registration_manual_activation: manuální aktivace účtu @@ -743,7 +741,6 @@ cs: label_age: Věk label_change_properties: Změnit vlastnosti label_general: Obecné - label_more: Více label_scm: SCM label_plugins: Doplňky label_ldap_authentication: Autentifikace LDAP @@ -753,7 +750,6 @@ cs: label_preferences: Nastavení label_chronological_order: V chronologickém pořadí label_reverse_chronological_order: V obrácaném chronologickém pořadí - label_planning: Plánování label_incoming_emails: Příchozí e-maily label_generate_key: Generovat klíč label_issue_watchers: Sledování @@ -974,16 +970,12 @@ cs: description_project_scope: Rozsah vyhledávání description_filter: Filtr description_user_mail_notification: Nastavení emailových notifikací - description_date_from: Zadejte počáteční datum description_message_content: Obsah zprávy description_available_columns: Dostupné sloupce - description_date_range_interval: Zvolte rozsah výběrem počátečního a koncového data description_issue_category_reassign: Zvolte kategorii úkolu description_search: Vyhledávací pole description_notes: Poznámky - description_date_range_list: Zvolte rozsah ze seznamu description_choose_project: Projekty - description_date_to: Zadejte datum description_query_sort_criteria_attribute: Třídící atribut description_wiki_subpages_reassign: Zvolte novou rodičovskou stránku description_selected_columns: Vybraný sloupec @@ -1207,5 +1199,31 @@ cs: setting_new_item_menu_tab: Záložka v menu projektu pro vytváření nových objektů label_new_object_tab_enabled: Zobrazit rozbalovací menu "+" error_no_projects_with_tracker_allowed_for_new_issue: Neexistují projekty s frontou, pro kterou lze vytvořit úkol + field_textarea_font: Písmo použité pro textová pole + label_font_default: Výchozí písmo + label_font_monospace: Neproporcionální písmo + label_font_proportional: Proporciální písmo + setting_timespan_format: Formát časového intervalu + label_table_of_contents: Obsah + setting_commit_logs_formatting: Použij textové formátování pro popisky comitů + setting_mail_handler_enable_regex_delimiters: Povol regulární výrazy + error_move_of_child_not_possible: 'Dílčí úkol %{child} nemůže být přesunut do nového + projektu: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Strávený čas nemůže být přiřazen k úkolu, který se bude mazat + setting_timelog_required_fields: Požadovaná pole pro zapisování času + label_attribute_of_object: "%{object_name} %{name}" + label_user_mail_option_only_assigned: Pouze pro věci, které sleduji nebo jsem na ně přiřazený + label_user_mail_option_only_owner: Pouze pro věci, které sleduji nebo jsem jejich vlastníkem + warning_fields_cleared_on_bulk_edit: Změny způsobí automatické smazání + hodnot z jednoho nebo více polí vybraných objektů + field_updated_by: Aktualizoval + field_last_updated_by: Naposledy změnil + field_full_width_layout: Celá šířka schematu + label_last_notes: Poslední poznámky + field_digest: Kontrolní součet + field_default_assigned_to: Výchozí přiřazený uživatel + setting_show_custom_fields_on_registration: Zobraz uživatelská pole při registraci + permission_view_news: Zobraz novinky + label_no_preview_alternative_html: "Náhled není k dispozici. Soubor: %{link}." + label_no_preview_download: Stažení diff --git a/config/locales/da.yml b/config/locales/da.yml index 7511d3303..57f6dfd71 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -142,6 +142,8 @@ da: circular_dependency: "Denne relation vil skabe et afhængighedsforhold" cant_link_an_issue_with_a_descendant: "En sag kan ikke relateres til en af dens underopgaver" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" template: header: @@ -455,7 +457,6 @@ da: label_internal: Intern label_last_changes: "sidste %{count} ændringer" label_change_view_all: Vis alle ændringer - label_personalize_page: Tilret denne side label_comment: Kommentar label_comment_plural: Kommentarer label_x_comments: @@ -598,7 +599,6 @@ da: label_age: Alder label_change_properties: Ændre indstillinger label_general: Generelt - label_more: Mere label_scm: SCM label_plugins: Plugins label_ldap_authentication: LDAP-godkendelse @@ -720,7 +720,6 @@ da: label_overall_activity: Overordnet aktivitet setting_default_projects_public: Nye projekter er offentlige som standard error_scm_annotate: "Filen findes ikke, eller kunne ikke annoteres." - label_planning: Planlægning text_subprojects_destroy_warning: "Dets underprojekter(er): %{value} vil også blive slettet." permission_edit_issues: Redigér sager setting_diff_max_lines_displayed: Højeste antal forskelle der vises @@ -912,7 +911,6 @@ da: error_can_not_remove_role: Denne rolle er i brug og kan ikke slettes. error_can_not_delete_tracker: Denne type indeholder sager og kan ikke slettes. field_principal: Principal - label_my_page_block: blok notice_failed_to_save_members: "Fejl under lagring af medlem(mer): %{errors}." text_zoom_out: Zoom ud text_zoom_in: Zoom ind @@ -923,10 +921,8 @@ da: project_module_calendar: Kalender button_edit_associated_wikipage: "Redigér tilknyttet Wiki side: %{page_title}" field_text: Tekstfelt - label_user_mail_option_only_owner: Kun for ting jeg er ejer af setting_default_notification_option: Standardpåmindelsesmulighed label_user_mail_option_only_my_events: Kun for ting jeg overvåger eller er involveret i - label_user_mail_option_only_assigned: Kun for ting jeg er tildelt label_user_mail_option_none: Ingen hændelser field_member_of_group: Medlem af gruppe field_assigned_to_role: Medlem af rolle @@ -984,16 +980,12 @@ da: description_project_scope: Search scope description_filter: Filter description_user_mail_notification: Mail notification settings - description_date_from: Enter start date description_message_content: Message content description_available_columns: Available Columns - description_date_range_interval: Choose range by selecting start and end date description_issue_category_reassign: Choose issue category description_search: Searchfield description_notes: Notes - description_date_range_list: Choose range from list description_choose_project: Projects - description_date_to: Enter end date description_query_sort_criteria_attribute: Sort attribute description_wiki_subpages_reassign: Choose new parent page description_selected_columns: Selected Columns @@ -1225,5 +1217,31 @@ da: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/de.yml b/config/locales/de.yml index 04f6753f4..f01b4d245 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -146,6 +146,8 @@ de: circular_dependency: "Diese Beziehung würde eine zyklische Abhängigkeit erzeugen" cant_link_an_issue_with_a_descendant: "Ein Ticket kann nicht mit einer Ihrer Unteraufgaben verlinkt werden" earlier_than_minimum_start_date: "kann wegen eines Vorgängertickets nicht vor %{date} liegen" + not_a_regexp: "Ist kein korrekter regulärer Ausdruck" + open_issue_with_closed_parent: "Ein offenes Ticket kann nicht an einen geschlossenen Vater gehängt werden" actionview_instancetag_blank_option: Bitte auswählen @@ -169,7 +171,7 @@ de: button_create_and_continue: Anlegen und weiter button_delete: Löschen button_delete_my_account: Mein Benutzerkonto löschen - button_download: Download + button_download: Herunterladen button_duplicate: Duplizieren button_edit: Bearbeiten button_edit_associated_wikipage: "Zugehörige Wikiseite bearbeiten: %{page_title}" @@ -227,10 +229,6 @@ de: description_all_columns: Alle Spalten description_available_columns: Verfügbare Spalten description_choose_project: Projekte - description_date_from: Startdatum eintragen - description_date_range_interval: Zeitraum durch Start- und Enddatum festlegen - description_date_range_list: Zeitraum aus einer Liste wählen - description_date_to: Enddatum eintragen description_filter: Filter description_issue_category_reassign: Neue Kategorie wählen description_message_content: Nachrichteninhalt @@ -262,13 +260,14 @@ de: error_no_tracker_in_project: Diesem Projekt ist kein Tracker zugeordnet. Bitte überprüfen Sie die Projekteinstellungen. error_scm_annotate: "Der Eintrag existiert nicht oder kann nicht annotiert werden." error_scm_annotate_big_text_file: Der Eintrag kann nicht umgesetzt werden, da er die maximale Textlänge überschreitet. - error_scm_command_failed: "Beim Zugriff auf das Projektarchiv ist ein Fehler aufgetreten: %{value}" - error_scm_not_found: Eintrag und/oder Revision existiert nicht im Projektarchiv. + error_scm_command_failed: "Beim Zugriff auf das Repository ist ein Fehler aufgetreten: %{value}" + error_scm_not_found: Eintrag und/oder Revision existiert nicht im Repository. error_session_expired: Ihre Sitzung ist abgelaufen. Bitte melden Sie sich erneut an. error_unable_delete_issue_status: "Der Ticket-Status konnte nicht gelöscht werden." error_unable_to_connect: Fehler beim Verbinden (%{value}) error_workflow_copy_source: Bitte wählen Sie einen Quell-Tracker und eine Quell-Rolle. error_workflow_copy_target: Bitte wählen Sie die Ziel-Tracker und -Rollen. + error_move_of_child_not_possible: "Unteraufgabe %{child} konnte nicht in das neue Projekt verschoben werden: %{errors}" field_account: Konto field_active: Aktiv @@ -375,7 +374,7 @@ de: field_subproject: Unterprojekt von field_summary: Zusammenfassung field_text: Textfeld - field_time_entries: Logzeit + field_time_entries: Aufwand buchen field_time_zone: Zeitzone field_timeout: Auszeit (in Sekunden) field_title: Titel @@ -390,6 +389,7 @@ de: field_visible: Sichtbar field_warn_on_leaving_unsaved: Vor dem Verlassen einer Seite mit ungesichertem Text im Editor warnen field_watcher: Beobachter + field_default_assigned_to: Standardbearbeiter general_csv_decimal_separator: ',' general_csv_encoding: ISO-8859-1 @@ -636,11 +636,9 @@ de: label_module_plural: Module label_month: Monat label_months_from: Monate ab - label_more: Mehr label_more_than_ago: vor mehr als label_my_account: Mein Konto label_my_page: Meine Seite - label_my_page_block: Verfügbare Widgets label_my_projects: Meine Projekte label_my_queries: Meine eigenen Abfragen label_new: Neu @@ -656,6 +654,8 @@ de: label_no_change_option: (Keine Änderung) label_no_data: Nichts anzuzeigen label_no_preview: Keine Vorschau verfügbar + label_no_preview_alternative_html: Keine Vorschau verfügbar. Sie können die Datei stattdessen %{link}. + label_no_preview_download: herunterladen label_no_issues_in_project: keine Tickets im Projekt label_nobody: Niemand label_none: kein @@ -673,8 +673,6 @@ de: label_password_required: Bitte geben Sie Ihr Passwort ein label_permissions: Berechtigungen label_permissions_report: Berechtigungsübersicht - label_personalize_page: Diese Seite anpassen - label_planning: Terminplanung label_please_login: Anmelden label_plugins: Plugins label_precedes: Vorgänger von @@ -710,9 +708,9 @@ de: label_report: Bericht label_report_plural: Berichte label_reported_issues: Erstellte Tickets - label_repository: Projektarchiv + label_repository: Repository label_repository_new: Neues Repository - label_repository_plural: Projektarchive + label_repository_plural: Repositories label_required: Erforderlich label_result_plural: Resultate label_reverse_chronological_order: in umgekehrter zeitlicher Reihenfolge @@ -725,7 +723,7 @@ de: label_roadmap_overdue: "seit %{value} verspätet" label_role: Rolle label_role_and_permissions: Rollen und Rechte - label_role_anonymous: Anonymous + label_role_anonymous: Anonym label_role_new: Neue Rolle label_role_non_member: Nichtmitglied label_role_plural: Rollen @@ -779,9 +777,7 @@ de: label_user_mail_no_self_notified: "Ich möchte nicht über Änderungen benachrichtigt werden, die ich selbst durchführe." label_user_mail_option_all: "Für alle Ereignisse in all meinen Projekten" label_user_mail_option_none: Keine Ereignisse - label_user_mail_option_only_assigned: Nur für Aufgaben für die ich zuständig bin label_user_mail_option_only_my_events: Nur für Aufgaben die ich beobachte oder an welchen ich mitarbeite - label_user_mail_option_only_owner: Nur für Aufgaben die ich angelegt habe label_user_mail_option_selected: "Für alle Ereignisse in den ausgewählten Projekten" label_user_new: Neuer Benutzer label_user_plural: Benutzer @@ -877,7 +873,7 @@ de: notice_email_sent: "Eine E-Mail wurde an %{value} gesendet." notice_failed_to_save_issues: "%{count} von %{total} ausgewählten Tickets konnte(n) nicht gespeichert werden: %{ids}." notice_failed_to_save_members: "Benutzer konnte nicht gespeichert werden: %{errors}." - notice_failed_to_save_time_entries: "Gescheitert %{count} Zeiteinträge für %{total} von ausgewählten: %{ids} zu speichern." + notice_failed_to_save_time_entries: "%{count} von %{total} ausgewählten Zeiteinträgen konnte(n) nicht gespeichert werden: %{ids}" notice_feeds_access_key_reseted: Ihr Atom-Zugriffsschlüssel wurde zurückgesetzt. notice_file_not_found: Anhang existiert nicht oder ist gelöscht worden. notice_gantt_chart_truncated: Die Grafik ist unvollständig, da das Maximum der anzeigbaren Aufgaben überschritten wurde (%{max}) @@ -888,7 +884,7 @@ de: notice_new_password_must_be_different: Das neue Passwort muss sich vom dem Aktuellen unterscheiden notice_no_issue_selected: "Kein Ticket ausgewählt! Bitte wählen Sie die Tickets, die Sie bearbeiten möchten." notice_not_authorized: Sie sind nicht berechtigt, auf diese Seite zuzugreifen. - notice_not_authorized_archived_project: Das Projekt wurde archiviert und ist daher nicht nicht verfügbar. + notice_not_authorized_archived_project: Das Projekt wurde archiviert und ist daher nicht verfügbar. notice_successful_connection: Verbindung erfolgreich. notice_successful_create: Erfolgreich angelegt notice_successful_delete: Erfolgreich gelöscht. @@ -904,7 +900,7 @@ de: permission_add_project: Projekt erstellen permission_add_subprojects: Unterprojekte erstellen permission_add_documents: Dokumente hinzufügen - permission_browse_repository: Projektarchiv ansehen + permission_browse_repository: Repository ansehen permission_close_project: Schließen / erneutes Öffnen eines Projekts permission_comment_news: News kommentieren permission_commit_access: Commit-Zugriff @@ -932,11 +928,12 @@ de: permission_manage_files: Dateien verwalten permission_manage_issue_relations: Ticket-Beziehungen verwalten permission_manage_members: Mitglieder verwalten + permission_view_news: News ansehen permission_manage_news: News verwalten permission_manage_project_activities: Aktivitäten (Zeiterfassung) verwalten permission_manage_public_queries: Öffentliche Filter verwalten permission_manage_related_issues: Zugehörige Tickets verwalten - permission_manage_repository: Projektarchiv verwalten + permission_manage_repository: Repository verwalten permission_manage_subtasks: Unteraufgaben verwalten permission_manage_versions: Versionen verwalten permission_manage_wiki: Wiki verwalten @@ -966,9 +963,9 @@ de: project_module_documents: Dokumente project_module_files: Dateien project_module_gantt: Gantt - project_module_issue_tracking: Ticket-Verfolgung + project_module_issue_tracking: Tickets project_module_news: News - project_module_repository: Projektarchiv + project_module_repository: Repository project_module_time_tracking: Zeiterfassung project_module_wiki: Wiki project_status_active: aktiv @@ -976,14 +973,14 @@ de: project_status_closed: geschlossen setting_activity_days_default: Anzahl Tage pro Seite der Projekt-Aktivität - setting_app_subtitle: Applikations-Untertitel - setting_app_title: Applikations-Titel + setting_app_subtitle: Applikationsuntertitel + setting_app_title: Applikationstitel setting_attachment_max_size: Max. Dateigröße setting_autofetch_changesets: Changesets automatisch abrufen setting_autologin: Automatische Anmeldung läuft ab nach setting_bcc_recipients: E-Mails als Blindkopie (BCC) senden setting_cache_formatted_text: Formatierten Text im Cache speichern - setting_commit_cross_project_ref: Erlauben auf Tickets aller anderen Projekte zu referenzieren + setting_commit_cross_project_ref: Erlauben Tickets aller anderen Projekte zu referenzieren setting_commit_fix_keywords: Schlüsselwörter (Status) setting_commit_logtime_activity_id: Aktivität für die Zeiterfassung setting_commit_logtime_enabled: Aktiviere Zeiterfassung via Commit-Nachricht @@ -1011,18 +1008,18 @@ de: setting_gravatar_enabled: Gravatar-Benutzerbilder benutzen setting_host_name: Hostname setting_issue_done_ratio: Berechne den Ticket-Fortschritt mittels - setting_issue_done_ratio_issue_field: Ticket-Feld %-erledigt + setting_issue_done_ratio_issue_field: Ticket-Feld % erledigt setting_issue_done_ratio_issue_status: Ticket-Status setting_issue_group_assignment: Ticketzuweisung an Gruppen erlauben setting_issue_list_default_columns: Standard-Spalten in der Ticket-Auflistung setting_issues_export_limit: Max. Anzahl Tickets bei CSV/PDF-Export setting_jsonp_enabled: JSONP Unterstützung aktivieren - setting_link_copied_issue: Tickets beim kopieren verlinken + setting_link_copied_issue: Tickets beim Kopieren verlinken setting_login_required: Authentifizierung erforderlich setting_mail_from: E-Mail-Absender setting_mail_handler_api_enabled: Abruf eingehender E-Mails aktivieren setting_mail_handler_api_key: API-Schlüssel für eingehende E-Mails - setting_sys_api_key: API-Schlüssel für Webservice zur Projektarchiv-Verwaltung + setting_sys_api_key: API-Schlüssel für Webservice zur Repository-Verwaltung setting_mail_handler_body_delimiters: "Schneide E-Mails nach einer dieser Zeilen ab" setting_mail_handler_excluded_filenames: Anhänge nach Namen ausschließen setting_new_project_user_role_id: Rolle, die einem Nicht-Administrator zugeordnet wird, der ein Projekt erstellt @@ -1038,15 +1035,17 @@ de: setting_repository_log_display_limit: Maximale Anzahl anzuzeigender Revisionen in der Historie einer Datei setting_rest_api_enabled: REST-Schnittstelle aktivieren setting_self_registration: Registrierung ermöglichen + setting_show_custom_fields_on_registration: Benutzerdefinierte Felder bei der Registrierung abfragen setting_sequential_project_identifiers: Fortlaufende Projektkennungen generieren setting_session_lifetime: Längste Dauer einer Sitzung setting_session_timeout: Zeitüberschreitung bei Inaktivität setting_start_of_week: Wochenanfang - setting_sys_api_enabled: Webservice zur Verwaltung der Projektarchive benutzen + setting_sys_api_enabled: Webservice zur Verwaltung der Repositories benutzen setting_text_formatting: Textformatierung setting_thumbnails_enabled: Vorschaubilder von Dateianhängen anzeigen setting_thumbnails_size: Größe der Vorschaubilder (in Pixel) setting_time_format: Zeitformat + setting_timespan_format: Format für Zeitspannen setting_unsubscribe: Erlaubt Benutzern das eigene Benutzerkonto zu löschen setting_user_format: Benutzer-Anzeigeformat setting_welcome_text: Willkommenstext @@ -1075,11 +1074,11 @@ de: text_git_repository_note: Repository steht für sich alleine (bare) und liegt lokal (z.B. /gitrepo, c:\gitrepo) text_issue_added: "Ticket %{id} wurde erstellt von %{author}." text_issue_category_destroy_assignments: Kategorie-Zuordnung entfernen - text_issue_category_destroy_question: "Einige Tickets (%{count}) sind dieser Kategorie zugeodnet. Was möchten Sie tun?" + text_issue_category_destroy_question: "Einige Tickets (%{count}) sind dieser Kategorie zugeordnet. Was möchten Sie tun?" text_issue_category_reassign_to: Tickets dieser Kategorie zuordnen - text_issue_conflict_resolution_add_notes: Meine Änderungen übernehmen und alle anderen Änderungen verwerfen - text_issue_conflict_resolution_cancel: Meine Änderungen verwerfen und %{link} neu anzeigen - text_issue_conflict_resolution_overwrite: Meine Änderungen trotzdem übernehmen (vorherige Notizen bleiben erhalten aber manche können überschrieben werden) + text_issue_conflict_resolution_add_notes: Nur meine Kommentare hinzufügen, meine übrigen Änderungen verwerfen + text_issue_conflict_resolution_cancel: Meine Kommentare und Änderungen verwerfen und %{link} neu anzeigen + text_issue_conflict_resolution_overwrite: Meine Änderungen trotzdem übernehmen (bisherige Kommentare bleiben bestehen, weitere Änderungen werden möglicherweise überschrieben) text_issue_updated: "Ticket %{id} wurde aktualisiert von %{author}." text_issues_destroy_confirmation: 'Sind Sie sicher, dass Sie die ausgewählten Tickets löschen möchten?' text_issues_destroy_descendants_confirmation: Dies wird auch %{count} Unteraufgabe/n löschen. @@ -1092,7 +1091,7 @@ de: text_length_between: "Länge zwischen %{min} und %{max} Zeichen." text_line_separated: Mehrere Werte sind erlaubt (eine Zeile pro Wert). text_load_default_configuration: Standard-Konfiguration laden - text_mercurial_repository_note: Lokales repository (e.g. /hgrepo, c:\hgrepo) + text_mercurial_repository_note: Lokales Repository (e.g. /hgrepo, c:\hgrepo) text_min_max_length_info: 0 heißt keine Beschränkung text_no_configuration_data: "Rollen, Tracker, Ticket-Status und Workflows wurden noch nicht konfiguriert.\nEs ist sehr zu empfehlen, die Standard-Konfiguration zu laden. Sobald sie geladen ist, können Sie diese abändern." text_own_membership_delete_confirmation: "Sie sind dabei, einige oder alle Ihre Berechtigungen zu entfernen. Es ist möglich, dass Sie danach das Projekt nicht mehr ansehen oder bearbeiten dürfen.\nSind Sie sicher, dass Sie dies tun möchten?" @@ -1103,7 +1102,7 @@ de: text_reassign_time_entries: 'Gebuchte Aufwände diesem Ticket zuweisen:' text_regexp_info: z. B. ^[A-Z0-9]+$ text_repository_identifier_info: 'Kleinbuchstaben (a-z), Ziffern, Binde- und Unterstriche erlaubt.
    Einmal gespeichert, kann die Kennung nicht mehr geändert werden.' - text_repository_usernames_mapping: "Bitte legen Sie die Zuordnung der Redmine-Benutzer zu den Benutzernamen der Commit-Nachrichten des Projektarchivs fest.\nBenutzer mit identischen Redmine- und Projektarchiv-Benutzernamen oder -E-Mail-Adressen werden automatisch zugeordnet." + text_repository_usernames_mapping: "Bitte legen Sie die Zuordnung der Redmine-Benutzer zu den Benutzernamen der Commit-Nachrichten des Repositories fest.\nBenutzer mit identischen Redmine- und Repository-Benutzernamen oder -E-Mail-Adressen werden automatisch zugeordnet." text_rmagick_available: RMagick verfügbar (optional) text_scm_command: Kommando text_scm_command_not_available: SCM-Kommando ist nicht verfügbar. Bitte prüfen Sie die Einstellungen im Administrationspanel. @@ -1209,5 +1208,22 @@ de: label_new_project_issue_tab_enabled: Tab "Neues Ticket" anzeigen setting_new_item_menu_tab: Menü zum Anlegen neuer Objekte label_new_object_tab_enabled: Dropdown-Menü "+" anzeigen + 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 + field_textarea_font: Schriftart für Textbereiche + label_font_default: Standardschrift + label_font_monospace: Nichtproportionale Schrift + label_font_proportional: Proportionale Schrift + setting_commit_logs_formatting: Textformatierung für Commit Nachrichten + setting_mail_handler_enable_regex_delimiters: Reguläre Ausdrücke erlauben error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Zeitbuchungen für Tickets, die gelöscht werden sind nicht möglich + setting_timelog_required_fields: Erforderliche Felder für Zeitbuchungen + label_attribute_of_object: '%{object_name}''s %{name}' + label_user_mail_option_only_assigned: Nur für Dinge, die ich beobachte oder die mir zugewiesen sind + label_user_mail_option_only_owner: Nur für Dinge, die ich beobachte oder die mir gehören + warning_fields_cleared_on_bulk_edit: Diese Änderungen werden eine automatische Löschung von ein oder mehreren Werten auf den selektierten Objekten zur Folge haben + field_updated_by: Geändert von + field_last_updated_by: Zuletzt geändert von + field_full_width_layout: Layout mit voller Breite + label_last_notes: Letzte Kommentare + field_digest: Checksumme diff --git a/config/locales/el.yml b/config/locales/el.yml index a28ae0c13..8c4d2f394 100644 --- a/config/locales/el.yml +++ b/config/locales/el.yml @@ -131,6 +131,8 @@ el: circular_dependency: "Αυτή η σχέση θα δημιουργήσει κυκλικές εξαρτήσεις" cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" actionview_instancetag_blank_option: Παρακαλώ επιλέξτε @@ -528,7 +530,6 @@ el: label_internal: Εσωτερικό label_last_changes: "Τελευταίες %{count} αλλαγές" label_change_view_all: Προβολή όλων των αλλαγών - label_personalize_page: Προσαρμογή σελίδας label_comment: Σχόλιο label_comment_plural: Σχόλια label_x_comments: @@ -680,7 +681,6 @@ el: label_age: Ηλικία label_change_properties: Αλλαγή ιδιοτήτων label_general: Γενικά - label_more: Περισσότερα label_scm: SCM label_plugins: Plugins label_ldap_authentication: Πιστοποίηση LDAP @@ -690,7 +690,6 @@ el: label_preferences: Προτιμήσεις label_chronological_order: Κατά χρονολογική σειρά label_reverse_chronological_order: Κατά αντίστροφη χρονολογική σειρά - label_planning: Σχεδιασμός label_incoming_emails: Εισερχόμενα email label_generate_key: Δημιουργία κλειδιού label_issue_watchers: Παρατηρητές @@ -896,7 +895,6 @@ el: error_can_not_remove_role: This role is in use and can not be deleted. error_can_not_delete_tracker: This tracker contains issues and cannot be deleted. field_principal: Principal - label_my_page_block: My page block notice_failed_to_save_members: "Failed to save member(s): %{errors}." text_zoom_out: Zoom out text_zoom_in: Zoom in @@ -907,10 +905,8 @@ el: project_module_calendar: Calendar button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}" field_text: Text field - label_user_mail_option_only_owner: Only for things I am the owner of setting_default_notification_option: Default notification option label_user_mail_option_only_my_events: Only for things I watch or I'm involved in - label_user_mail_option_only_assigned: Only for things I am assigned to label_user_mail_option_none: No events field_member_of_group: Assignee's group field_assigned_to_role: Assignee's role @@ -967,16 +963,12 @@ el: description_project_scope: Search scope description_filter: Filter description_user_mail_notification: Mail notification settings - description_date_from: Enter start date description_message_content: Message content description_available_columns: Available Columns - description_date_range_interval: Choose range by selecting start and end date description_issue_category_reassign: Choose issue category description_search: Searchfield description_notes: Notes - description_date_range_list: Choose range from list description_choose_project: Projects - description_date_to: Enter end date description_query_sort_criteria_attribute: Sort attribute description_wiki_subpages_reassign: Choose new parent page description_selected_columns: Selected Columns @@ -1208,5 +1200,31 @@ el: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index 7e7271fec..2723241c4 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -134,6 +134,8 @@ en-GB: circular_dependency: "This relation would create a circular dependency" cant_link_an_issue_with_a_descendant: "An issue cannot be linked to one of its subtasks" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" actionview_instancetag_blank_option: Please select @@ -498,7 +500,6 @@ en-GB: label_my_page: My page label_my_account: My account label_my_projects: My projects - label_my_page_block: My page block label_administration: Administration label_login: Sign in label_logout: Sign out @@ -588,7 +589,6 @@ en-GB: label_internal: Internal label_last_changes: "last %{count} changes" label_change_view_all: View all changes - label_personalize_page: Personalise this page label_comment: Comment label_comment_plural: Comments label_x_comments: @@ -691,8 +691,8 @@ en-GB: label_relation_new: New relation label_relation_delete: Delete relation label_relates_to: related to - label_duplicates: duplicates - label_duplicated_by: duplicated by + label_duplicates: is duplicate of + label_duplicated_by: has duplicate label_blocks: blocks label_blocked_by: blocked by label_precedes: precedes @@ -741,8 +741,6 @@ en-GB: label_user_mail_option_selected: "For any event on the selected projects only..." label_user_mail_option_none: "No events" label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in" - label_user_mail_option_only_assigned: "Only for things I am assigned to" - label_user_mail_option_only_owner: "Only for things I am the owner of" label_user_mail_no_self_notified: "I don't want to be notified of changes that I make myself" label_registration_activation_by_email: account activation by email label_registration_manual_activation: manual account activation @@ -751,7 +749,6 @@ en-GB: label_age: Age label_change_properties: Change properties label_general: General - label_more: More label_scm: SCM label_plugins: Plugins label_ldap_authentication: LDAP authentication @@ -761,7 +758,6 @@ en-GB: label_preferences: Preferences label_chronological_order: In chronological order label_reverse_chronological_order: In reverse chronological order - label_planning: Planning label_incoming_emails: Incoming emails label_generate_key: Generate a key label_issue_watchers: Watchers @@ -976,16 +972,12 @@ en-GB: description_project_scope: Search scope description_filter: Filter description_user_mail_notification: Mail notification settings - description_date_from: Enter start date description_message_content: Message content description_available_columns: Available Columns - description_date_range_interval: Choose range by selecting start and end date description_issue_category_reassign: Choose issue category description_search: Searchfield description_notes: Notes - description_date_range_list: Choose range from list description_choose_project: Projects - description_date_to: Enter end date description_query_sort_criteria_attribute: Sort attribute description_wiki_subpages_reassign: Choose new parent page description_selected_columns: Selected Columns @@ -1210,5 +1202,31 @@ en-GB: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/en.yml b/config/locales/en.yml index 399c2e85a..df57a57f3 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -130,6 +130,8 @@ en: circular_dependency: "This relation would create a circular dependency" cant_link_an_issue_with_a_descendant: "An issue cannot be linked to one of its subtasks" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" actionview_instancetag_blank_option: Please select @@ -215,7 +217,9 @@ en: error_ldap_bind_credentials: "Invalid LDAP Account/Password" error_no_tracker_allowed_for_new_issue_in_project: "The project doesn't have any trackers for which you can create an issue" error_no_projects_with_tracker_allowed_for_new_issue: "There are no projects with trackers for which you can create an issue" + error_move_of_child_not_possible: "Subtask %{child} could not be moved to the new project: %{errors}" error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: "Spent time cannot be reassigned to an issue that is about to be deleted" + warning_fields_cleared_on_bulk_edit: "Changes will result in the automatic deletion of values from one or more fields on the selected objects" mail_subject_lost_password: "Your %{value} password" mail_body_lost_password: 'To change your password, click on the following link:' @@ -365,6 +369,12 @@ en: field_total_estimated_hours: Total estimated time field_default_version: Default version field_remote_ip: IP address + field_textarea_font: Font used for text areas + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + field_digest: Checksum + field_default_assigned_to: Default assignee setting_app_title: Application title setting_app_subtitle: Application subtitle @@ -372,6 +382,7 @@ en: setting_default_language: Default language setting_login_required: Authentication required setting_self_registration: Self-registration + setting_show_custom_fields_on_registration: Show custom fields on registration setting_attachment_max_size: Maximum attachment size setting_issues_export_limit: Issues export limit setting_mail_from: Emission email address @@ -389,6 +400,7 @@ en: setting_autologin: Autologin setting_date_format: Date format setting_time_format: Time format + setting_timespan_format: Time span format setting_cross_project_issue_relations: Allow cross-project issue relations setting_cross_project_subtasks: Allow cross-project subtasks setting_issue_list_default_columns: Default columns displayed on the issue list @@ -402,6 +414,7 @@ en: setting_display_subprojects_issues: Display subprojects issues on main projects by default setting_enabled_scm: Enabled SCM setting_mail_handler_body_delimiters: "Truncate emails after one of these lines" + setting_mail_handler_enable_regex_delimiters: "Enable regular expressions" setting_mail_handler_api_enabled: Enable WS for incoming emails setting_mail_handler_api_key: Incoming email WS API key setting_sys_api_key: Repository management WS API key @@ -447,6 +460,8 @@ en: setting_attachment_extensions_allowed: Allowed extensions setting_attachment_extensions_denied: Disallowed extensions setting_new_item_menu_tab: Project menu tab for creating new objects + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_timelog_required_fields: Required fields for time logs permission_add_project: Create project permission_add_subprojects: Create subprojects @@ -482,6 +497,7 @@ en: permission_view_time_entries: View spent time permission_edit_time_entries: Edit time logs permission_edit_own_time_entries: Edit own time logs + permission_view_news: View news permission_manage_news: Manage news permission_comment_news: Comment news permission_view_documents: View documents @@ -589,7 +605,6 @@ en: label_my_page: My page label_my_account: My account label_my_projects: My projects - label_my_page_block: My page block label_administration: Administration label_login: Sign in label_logout: Sign out @@ -624,6 +639,8 @@ en: label_attribute_plural: Attributes label_no_data: No data to display label_no_preview: No preview available + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download label_change_status: Change status label_history: History label_attachment: File @@ -687,7 +704,6 @@ en: label_internal: Internal label_last_changes: "last %{count} changes" label_change_view_all: View all changes - label_personalize_page: Personalize this page label_comment: Comment label_comment_plural: Comments label_x_comments: @@ -804,8 +820,8 @@ en: label_relation_new: New relation label_relation_delete: Delete relation label_relates_to: Related to - label_duplicates: Duplicates - label_duplicated_by: Duplicated by + label_duplicates: Is duplicate of + label_duplicated_by: Has duplicate label_blocks: Blocks label_blocked_by: Blocked by label_precedes: Precedes @@ -857,8 +873,8 @@ en: label_user_mail_option_selected: "For any event on the selected projects only..." label_user_mail_option_none: "No events" label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in" - label_user_mail_option_only_assigned: "Only for things I am assigned to" - label_user_mail_option_only_owner: "Only for things I am the owner of" + label_user_mail_option_only_assigned: "Only for things I watch or I am assigned to" + label_user_mail_option_only_owner: "Only for things I watch or I am the owner of" label_user_mail_no_self_notified: "I don't want to be notified of changes that I make myself" label_registration_activation_by_email: account activation by email label_registration_manual_activation: manual account activation @@ -867,7 +883,6 @@ en: label_age: Age label_change_properties: Change properties label_general: General - label_more: More label_scm: SCM label_plugins: Plugins label_ldap_authentication: LDAP authentication @@ -877,7 +892,6 @@ en: label_preferences: Preferences label_chronological_order: In chronological order label_reverse_chronological_order: In reverse chronological order - label_planning: Planning label_incoming_emails: Incoming emails label_generate_key: Generate a key label_issue_watchers: Watchers @@ -940,6 +954,7 @@ en: label_attribute_of_assigned_to: "Assignee's %{name}" label_attribute_of_user: "User's %{name}" label_attribute_of_fixed_version: "Target version's %{name}" + label_attribute_of_object: "%{object_name}'s %{name}" label_cross_project_descendants: With subprojects label_cross_project_tree: With project tree label_cross_project_hierarchy: With project hierarchy @@ -999,6 +1014,11 @@ en: label_relations: Relations label_new_project_issue_tab_enabled: Display the "New issue" tab label_new_object_tab_enabled: Display the "+" drop-down + label_table_of_contents: Table of contents + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + label_last_notes: Last notes button_login: Login button_submit: Submit @@ -1188,8 +1208,4 @@ en: description_all_columns: All Columns description_issue_category_reassign: Choose issue category description_wiki_subpages_reassign: Choose new parent page - description_date_range_list: Choose range from list - description_date_range_interval: Choose range by selecting start and end date - description_date_from: Enter start date - description_date_to: Enter end date text_repository_identifier_info: 'Only lower case letters (a-z), numbers, dashes and underscores are allowed.
    Once saved, the identifier cannot be changed.' diff --git a/config/locales/es-PA.yml b/config/locales/es-PA.yml index adefd608c..ef2be86ce 100644 --- a/config/locales/es-PA.yml +++ b/config/locales/es-PA.yml @@ -140,6 +140,8 @@ es-PA: circular_dependency: "Esta relación podría crear una dependencia circular" cant_link_an_issue_with_a_descendant: "Esta incidencia no puede ser ligada a una de estas tareas" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" # Append your own errors here or at the model/attributes scope. @@ -479,7 +481,7 @@ es-PA: label_issue_category: Categoría de las incidencias label_issue_category_new: Nueva categoría label_issue_category_plural: Categorías de las incidencias - label_issue_new: Nueva incidencias + label_issue_new: Nueva incidencia label_issue_plural: Incidencias label_issue_status: Estado de la incidencias label_issue_status_new: Nuevo estado @@ -519,7 +521,6 @@ es-PA: label_module_plural: Módulos label_month: Mes label_months_from: meses de - label_more: Más label_more_than_ago: hace más de label_my_account: Mi cuenta label_my_page: Mi página @@ -548,8 +549,6 @@ es-PA: label_password_lost: ¿Olvidaste la contraseña? label_permissions: Permisos label_permissions_report: Informe de permisos - label_personalize_page: Personalizar esta página - label_planning: Planificación label_please_login: Por favor, inicie sesión label_plugins: Extensiones label_precedes: anterior a @@ -937,7 +936,6 @@ es-PA: error_can_not_remove_role: Este rol está en uso y no puede ser eliminado. error_can_not_delete_tracker: Este tipo contiene incidencias y no puede ser eliminado. field_principal: Principal - label_my_page_block: Bloque Mi página notice_failed_to_save_members: "Fallo al guardar miembro(s): %{errors}." text_zoom_out: Alejar text_zoom_in: Acercar @@ -948,10 +946,8 @@ es-PA: project_module_calendar: Calendario button_edit_associated_wikipage: "Editar paginas Wiki asociadas: %{page_title}" field_text: Campo de texto - label_user_mail_option_only_owner: Solo para objetos que soy propietario setting_default_notification_option: Opcion de notificacion por defecto label_user_mail_option_only_my_events: Solo para objetos que soy seguidor o estoy involucrado - label_user_mail_option_only_assigned: Solo para objetos que estoy asignado label_user_mail_option_none: Sin eventos field_member_of_group: Asignado al grupo field_assigned_to_role: Asignado al perfil @@ -1012,16 +1008,12 @@ es-PA: description_project_scope: Ámbito de búsqueda description_filter: Filtro description_user_mail_notification: Configuración de notificaciones por correo - description_date_from: Introduzca la fecha de inicio description_message_content: Contenido del mensaje description_available_columns: Columnas disponibles - description_date_range_interval: Elija el rango seleccionando la fecha de inicio y fin description_issue_category_reassign: Elija la categoría de la incidencia description_search: Campo de búsqueda description_notes: Notas - description_date_range_list: Elija el rango en la lista description_choose_project: Proyectos - description_date_to: Introduzca la fecha fin description_query_sort_criteria_attribute: Atributo de ordenación description_wiki_subpages_reassign: Elija la nueva página padre description_selected_columns: Columnas seleccionadas @@ -1238,5 +1230,31 @@ es-PA: setting_new_item_menu_tab: Pestaña de creación de nuevos objetos en el menú de cada proyecto label_new_object_tab_enabled: Mostrar la lista desplegable "+" error_no_projects_with_tracker_allowed_for_new_issue: Ningún proyecto dispone de un tipo sobre el cual puedas crear una petición + field_textarea_font: Fuente usada en las áreas de texto + label_font_default: Fuente por defecto + label_font_monospace: Fuente Monospaced + label_font_proportional: Fuente Proportional + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/es.yml b/config/locales/es.yml index fe8389869..efc8c2b26 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -138,6 +138,8 @@ es: circular_dependency: "Esta relación podría crear una dependencia circular" cant_link_an_issue_with_a_descendant: "Esta petición no puede ser ligada a una de estas tareas" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" # Append your own errors here or at the model/attributes scope. @@ -517,7 +519,6 @@ es: label_module_plural: Módulos label_month: Mes label_months_from: meses de - label_more: Más label_more_than_ago: hace más de label_my_account: Mi cuenta label_my_page: Mi página @@ -546,8 +547,6 @@ es: label_password_lost: ¿Olvidaste la contraseña? label_permissions: Permisos label_permissions_report: Informe de permisos - label_personalize_page: Personalizar esta página - label_planning: Planificación label_please_login: Por favor, inicie sesión label_plugins: Extensiones label_precedes: anterior a @@ -766,7 +765,7 @@ es: setting_mail_handler_api_enabled: Activar SW para mensajes entrantes setting_mail_handler_api_key: Clave de la API setting_per_page_options: Objetos por página - setting_plain_text_mail: sólo texto plano (no HTML) + setting_plain_text_mail: Sólo texto plano (no HTML) setting_protocol: Protocolo setting_self_registration: Registro permitido setting_sequential_project_identifiers: Generar identificadores de proyecto @@ -935,7 +934,6 @@ es: error_can_not_remove_role: Este rol está en uso y no puede ser eliminado. error_can_not_delete_tracker: Este tipo contiene peticiones y no puede ser eliminado. field_principal: Principal - label_my_page_block: Bloque Mi página notice_failed_to_save_members: "Fallo al guardar miembro(s): %{errors}." text_zoom_out: Alejar text_zoom_in: Acercar @@ -946,10 +944,8 @@ es: project_module_calendar: Calendario button_edit_associated_wikipage: "Editar paginas Wiki asociadas: %{page_title}" field_text: Campo de texto - label_user_mail_option_only_owner: Solo para objetos que soy propietario - setting_default_notification_option: Opcion de notificacion por defecto + setting_default_notification_option: Opción de notificación por defecto label_user_mail_option_only_my_events: Solo para objetos que soy seguidor o estoy involucrado - label_user_mail_option_only_assigned: Solo para objetos que estoy asignado label_user_mail_option_none: Sin eventos field_member_of_group: Asignado al grupo field_assigned_to_role: Asignado al perfil @@ -1010,16 +1006,12 @@ es: description_project_scope: Ámbito de búsqueda description_filter: Filtro description_user_mail_notification: Configuración de notificaciones por correo - description_date_from: Introduzca la fecha de inicio description_message_content: Contenido del mensaje description_available_columns: Columnas disponibles - description_date_range_interval: Elija el rango seleccionando la fecha de inicio y fin description_issue_category_reassign: Elija la categoría de la petición description_search: Campo de búsqueda description_notes: Notas - description_date_range_list: Elija el rango en la lista description_choose_project: Proyectos - description_date_to: Introduzca la fecha fin description_query_sort_criteria_attribute: Atributo de ordenación description_wiki_subpages_reassign: Elija la nueva página padre description_selected_columns: Columnas seleccionadas @@ -1158,7 +1150,7 @@ es: label_search_attachments_yes: Buscar adjuntos por nombre de archivo y descripciones label_search_attachments_no: No buscar adjuntos label_search_attachments_only: Sólo Buscar adjuntos - label_search_open_issues_only: Sólo Abrir Peticiones + label_search_open_issues_only: Sólo Peticiones Abiertas field_address: Correo electrónico setting_max_additional_emails: Número Máximo de correos electrónicos adicionales label_email_address_plural: Correo Electrónicos @@ -1236,5 +1228,31 @@ es: setting_new_item_menu_tab: Pestaña de creación de nuevos objetos en el menú de cada proyecto label_new_object_tab_enabled: Mostrar la lista desplegable "+" error_no_projects_with_tracker_allowed_for_new_issue: Ningún proyecto dispone de un tipo sobre el cual puedas crear una petición + field_textarea_font: Fuente usada en las áreas de texto + label_font_default: Fuente por defecto + label_font_monospace: Fuente Monospaced + label_font_proportional: Fuente Proportional + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/et.yml b/config/locales/et.yml index 117bed5ed..ca699441f 100644 --- a/config/locales/et.yml +++ b/config/locales/et.yml @@ -147,6 +147,8 @@ et: circular_dependency: "See suhe looks vastastikuse sõltuvuse" cant_link_an_issue_with_a_descendant: "Teemat ei saa sidustada tema enda alamteemaga" earlier_than_minimum_start_date: "Tähtpäev ei saa olla varasem kui %{date} eelnevate teemade tähtpäevade tõttu" + 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" actionview_instancetag_blank_option: "Palun vali" @@ -536,7 +538,6 @@ et: label_my_page: "Minu leht" label_my_account: "Minu konto" label_my_projects: "Minu projektid" - label_my_page_block: "Uus blokk" label_administration: "Seadistused" label_login: "Logi sisse" label_logout: "Logi välja" @@ -629,7 +630,6 @@ et: label_internal: "Sisemine" label_last_changes: "viimased %{count} muudatust" label_change_view_all: "Kõik muudatused" - label_personalize_page: "Kujunda leht ümber" label_comment: "Kommentaar" label_comment_plural: "Kommentaarid" label_x_comments: @@ -786,8 +786,6 @@ et: label_user_mail_option_selected: "Kõigist tegevustest ainult valitud projektides..." label_user_mail_option_none: "Teavitusi ei saadeta" label_user_mail_option_only_my_events: "Ainult mu jälgitavatest või minuga seotud tegevustest" - label_user_mail_option_only_assigned: "Ainult minu teha olevate asjade kohta" - label_user_mail_option_only_owner: "Ainult mu oma asjade kohta" label_user_mail_no_self_notified: "Ära teavita mind mu enda tehtud muudatustest" label_registration_activation_by_email: "e-kirjaga" label_registration_manual_activation: "käsitsi" @@ -796,7 +794,6 @@ et: label_age: "Vanus" label_change_properties: "Muuda omadusi" label_general: "Üldine" - label_more: "Rohkem" label_scm: "Lähtekoodi haldusvahend" label_plugins: "Lisamoodulid" label_ldap_authentication: "LDAP autentimine" @@ -806,7 +803,6 @@ et: label_preferences: "Eelistused" label_chronological_order: "kronoloogiline" label_reverse_chronological_order: "tagurpidi kronoloogiline" - label_planning: "Planeerimine" label_incoming_emails: "Sissetulevad e-kirjad" label_generate_key: "Genereeri võti" label_issue_watchers: "Jälgijad" @@ -1028,10 +1024,6 @@ et: description_all_columns: "Kõik veerud" description_issue_category_reassign: "Vali uus kategooria" description_wiki_subpages_reassign: "Vali lehele uus vanem" - description_date_range_list: "Vali vahemik nimekirjast" - description_date_range_interval: "Vali vahemik algus- ja lõpukuupäeva abil" - description_date_from: "Sisesta alguskuupäev" - description_date_to: "Sisesta lõpukuupäev" error_session_expired: "Sinu sessioon on aegunud. Palun logi uuesti sisse" text_session_expiration_settings: "Hoiatus! Nende sätete muutmine võib kehtivad sessioonid lõpetada. Ka sinu enda oma!" setting_session_lifetime: "Sessiooni eluiga" @@ -1213,5 +1205,31 @@ et: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/eu.yml b/config/locales/eu.yml index 20b206695..8127a32c0 100644 --- a/config/locales/eu.yml +++ b/config/locales/eu.yml @@ -132,6 +132,8 @@ eu: circular_dependency: "Erlazio honek mendekotasun zirkular bat sortuko luke" cant_link_an_issue_with_a_descendant: "Zeregin bat ezin da bere azpiataza batekin estekatu." earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" actionview_instancetag_blank_option: Hautatu mesedez @@ -551,7 +553,6 @@ eu: label_internal: Barnekoa label_last_changes: "azken %{count} aldaketak" label_change_view_all: Aldaketa guztiak ikusi - label_personalize_page: Orri hau pertsonalizatu label_comment: Iruzkin label_comment_plural: Iruzkinak label_x_comments: @@ -706,7 +707,6 @@ eu: label_age: Adina label_change_properties: Propietateak aldatu label_general: Orokorra - label_more: Gehiago label_scm: IKK label_plugins: Pluginak label_ldap_authentication: LDAP autentikazioa @@ -716,7 +716,6 @@ eu: label_preferences: Hobespenak label_chronological_order: Orden kronologikoan label_reverse_chronological_order: Alderantzizko orden kronologikoan - label_planning: Planifikazioa label_incoming_emails: Sarrerako epostak label_generate_key: Giltza sortu label_issue_watchers: Behatzaileak @@ -897,7 +896,6 @@ eu: error_can_not_remove_role: Rol hau erabiltzen hari da eta ezin da ezabatu. error_can_not_delete_tracker: Aztarnari honek zereginak ditu eta ezin da ezabatu. field_principal: Ekintzaile - label_my_page_block: "Nire orriko blokea" notice_failed_to_save_members: "Kidea(k) gordetzean errorea: %{errors}." text_zoom_out: Zooma txikiagotu text_zoom_in: Zooma handiagotu @@ -908,10 +906,8 @@ eu: project_module_calendar: Egutegia button_edit_associated_wikipage: "Esleitutako wiki orria editatu: %{page_title}" field_text: Testu eremua - label_user_mail_option_only_owner: "Jabea naizen gauzetarako barrarik" setting_default_notification_option: "Lehenetsitako ohartarazpen aukera" label_user_mail_option_only_my_events: "Behatzen ditudan edo partaide naizen gauzetarako bakarrik" - label_user_mail_option_only_assigned: "Niri esleitutako gauzentzat bakarrik" label_user_mail_option_none: "Gertakaririk ez" field_member_of_group: "Esleituta duenaren taldea" field_assigned_to_role: "Esleituta duenaren rola" @@ -969,16 +965,12 @@ eu: description_project_scope: Search scope description_filter: Filter description_user_mail_notification: Mail notification settings - description_date_from: Enter start date description_message_content: Message content description_available_columns: Available Columns - description_date_range_interval: Choose range by selecting start and end date description_issue_category_reassign: Choose issue category description_search: Searchfield description_notes: Notes - description_date_range_list: Choose range from list description_choose_project: Projects - description_date_to: Enter end date description_query_sort_criteria_attribute: Sort attribute description_wiki_subpages_reassign: Choose new parent page description_selected_columns: Selected Columns @@ -1209,5 +1201,31 @@ eu: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/fa.yml b/config/locales/fa.yml index e8404cf0b..5ee5b5fc5 100644 --- a/config/locales/fa.yml +++ b/config/locales/fa.yml @@ -130,6 +130,8 @@ fa: circular_dependency: "این وابستگی یک وابستگی دایره وار خواهد ساخت" cant_link_an_issue_with_a_descendant: "یک مورد نمی‌تواند به یکی از زیر کارهایش پیوند بخورد" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" actionview_instancetag_blank_option: گزینش کنید @@ -487,7 +489,6 @@ fa: label_my_page: صفحه خودم label_my_account: تنظیمات خودم label_my_projects: پروژه‌های خودم - label_my_page_block: بخش صفحه خودم label_administration: مدیریت label_login: نام کاربری label_logout: خروج @@ -575,7 +576,6 @@ fa: label_internal: درونی label_last_changes: "%{count} تغییر آخر" label_change_view_all: دیدن همه تغییرات - label_personalize_page: سفارشی نمودن این صفحه label_comment: نظر label_comment_plural: نظر ها label_x_comments: @@ -727,8 +727,6 @@ fa: label_user_mail_option_selected: "برای هر خبر تنها در پروژه‌های گزینش شده..." label_user_mail_option_none: "هیچ خبری" label_user_mail_option_only_my_events: "تنها برای چیزهایی که دیده‌بان هستم یا در آن‌ها درگیر هستم" - label_user_mail_option_only_assigned: "تنها برای چیزهایی که به من واگذار شده" - label_user_mail_option_only_owner: "تنها برای چیزهایی که من دارنده آن‌ها هستم" label_user_mail_no_self_notified: "نمی‌خواهم از تغییراتی که خودم می‌دهم آگاه شوم" label_registration_activation_by_email: فعالسازی حساب با ایمیل label_registration_manual_activation: فعالسازی حساب دستی @@ -737,7 +735,6 @@ fa: label_age: سن label_change_properties: ویرایش ویژگی‌ها label_general: همگانی - label_more: بیشتر label_scm: SCM label_plugins: افزونه‌ها label_ldap_authentication: شناساییLDAP @@ -747,7 +744,6 @@ fa: label_preferences: پسندها label_chronological_order: به ترتیب تاریخ label_reverse_chronological_order: برعکس ترتیب تاریخ - label_planning: برنامه ریزی label_incoming_emails: ایمیل‌های آمده label_generate_key: ساخت کلید label_issue_watchers: دیده‌بان‌ها @@ -969,16 +965,12 @@ fa: description_project_scope: Search scope description_filter: Filter description_user_mail_notification: Mail notification settings - description_date_from: Enter start date description_message_content: Message content description_available_columns: Available Columns - description_date_range_interval: Choose range by selecting start and end date description_issue_category_reassign: Choose issue category description_search: Searchfield description_notes: Notes - description_date_range_list: Choose range from list description_choose_project: Projects - description_date_to: Enter end date description_query_sort_criteria_attribute: Sort attribute description_wiki_subpages_reassign: Choose new parent page description_selected_columns: Selected Columns @@ -1209,5 +1201,31 @@ fa: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/fi.yml b/config/locales/fi.yml index acafa9b86..7d69d6cfc 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -155,6 +155,8 @@ fi: circular_dependency: "Tämä suhde loisi kehän." cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" actionview_instancetag_blank_option: Valitse, ole hyvä @@ -449,7 +451,6 @@ fi: label_internal: Sisäinen label_last_changes: "viimeiset %{count} muutokset" label_change_view_all: Näytä kaikki muutokset - label_personalize_page: Personoi tämä sivu label_comment: Kommentti label_comment_plural: Kommentit label_x_comments: @@ -676,7 +677,6 @@ fi: setting_user_format: Käyttäjien esitysmuoto text_status_changed_by_changeset: "Päivitetty muutosversioon %{value}." text_issues_destroy_confirmation: 'Oletko varma että haluat poistaa valitut tapahtumat ?' - label_more: Lisää label_issue_added: Tapahtuma lisätty label_issue_updated: Tapahtuma päivitetty label_document_added: Dokumentti lisätty @@ -727,7 +727,6 @@ fi: setting_default_projects_public: Uudet projektit ovat oletuksena julkisia label_overall_activity: Kokonaishistoria error_scm_annotate: "Merkintää ei ole tai siihen ei voi lisätä selityksiä." - label_planning: Suunnittelu text_subprojects_destroy_warning: "Tämän aliprojekti(t): %{value} tullaan myös poistamaan." label_and_its_subprojects: "%{value} ja aliprojektit" mail_body_reminder: "%{count} sinulle nimettyä tapahtuma(a) erääntyy %{days} päivä sisään:" @@ -917,7 +916,6 @@ fi: error_can_not_remove_role: This role is in use and can not be deleted. error_can_not_delete_tracker: This tracker contains issues and cannot be deleted. field_principal: Principal - label_my_page_block: My page block notice_failed_to_save_members: "Failed to save member(s): %{errors}." text_zoom_out: Zoom out text_zoom_in: Zoom in @@ -928,10 +926,8 @@ fi: project_module_calendar: Calendar button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}" field_text: Text field - label_user_mail_option_only_owner: Only for things I am the owner of setting_default_notification_option: Default notification option label_user_mail_option_only_my_events: Only for things I watch or I'm involved in - label_user_mail_option_only_assigned: Only for things I am assigned to label_user_mail_option_none: No events field_member_of_group: Assignee's group field_assigned_to_role: Assignee's role @@ -988,16 +984,12 @@ fi: description_project_scope: Search scope description_filter: Filter description_user_mail_notification: Mail notification settings - description_date_from: Enter start date description_message_content: Message content description_available_columns: Available Columns - description_date_range_interval: Choose range by selecting start and end date description_issue_category_reassign: Choose issue category description_search: Searchfield description_notes: Notes - description_date_range_list: Choose range from list description_choose_project: Projects - description_date_to: Enter end date description_query_sort_criteria_attribute: Sort attribute description_wiki_subpages_reassign: Choose new parent page description_selected_columns: Selected Columns @@ -1229,5 +1221,31 @@ fi: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 502b12040..dea9e3e09 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -150,6 +150,8 @@ fr: circular_dependency: "Cette relation créerait une dépendance circulaire" cant_link_an_issue_with_a_descendant: "Une demande ne peut pas être liée à l'une de ses sous-tâches" earlier_than_minimum_start_date: "ne peut pas être antérieure au %{date} à cause des demandes qui précèdent" + not_a_regexp: "n'est pas une expression regulière valide" + open_issue_with_closed_parent: "Une demande ouverte ne peut pas être rattachée à une demande fermée" actionview_instancetag_blank_option: Choisir @@ -235,7 +237,9 @@ fr: error_ldap_bind_credentials: "Identifiant ou mot de passe LDAP incorrect" error_no_tracker_allowed_for_new_issue_in_project: "Le projet ne dispose d'aucun tracker sur lequel vous pouvez créer une demande" error_no_projects_with_tracker_allowed_for_new_issue: "Aucun projet ne dispose d'un tracker sur lequel vous pouvez créer une demande" + error_move_of_child_not_possible: "La sous-tâche %{child} n'a pas pu être déplacée dans le nouveau projet : %{errors}" error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: "Le temps passé ne peut pas être réaffecté à une demande qui va être supprimée" + warning_fields_cleared_on_bulk_edit: "Les changements apportés entraîneront la suppression automatique des valeurs d'un ou plusieurs champs sur les objets sélectionnés" mail_subject_lost_password: "Votre mot de passe %{value}" mail_body_lost_password: 'Pour changer votre mot de passe, cliquez sur le lien suivant :' @@ -377,6 +381,12 @@ fr: field_time_entries_visibility: Visibilité du temps passé field_total_estimated_hours: Temps estimé total field_default_version: Version par défaut + field_textarea_font: Police utilisée pour les champs texte + field_updated_by: Mise à jour par + field_last_updated_by: Dernière mise à jour par + field_full_width_layout: Afficher sur toute la largeur + field_digest: Checksum + field_default_assigned_to: Assigné par défaut setting_app_title: Titre de l'application setting_app_subtitle: Sous-titre de l'application @@ -384,6 +394,7 @@ fr: setting_default_language: Langue par défaut setting_login_required: Authentification obligatoire setting_self_registration: Inscription des nouveaux utilisateurs + setting_show_custom_fields_on_registration: Afficher les champs personnalisés sur le formulaire d'inscription setting_attachment_max_size: Taille maximale des fichiers setting_issues_export_limit: Limite d'exportation des demandes setting_mail_from: Adresse d'émission @@ -401,6 +412,7 @@ fr: setting_autologin: Durée maximale de connexion automatique setting_date_format: Format de date setting_time_format: Format d'heure + setting_timespan_format: Format des temps en heures setting_cross_project_issue_relations: Autoriser les relations entre demandes de différents projets setting_cross_project_subtasks: Autoriser les sous-tâches dans des projets différents setting_issue_list_default_columns: Colonnes affichées par défaut sur la liste des demandes @@ -414,6 +426,7 @@ fr: setting_display_subprojects_issues: Afficher par défaut les demandes des sous-projets sur les projets principaux setting_enabled_scm: SCM activés setting_mail_handler_body_delimiters: "Tronquer les emails après l'une de ces lignes" + setting_mail_handler_enable_regex_delimiters: "Utiliser les expressions regulières" setting_mail_handler_api_enabled: "Activer le WS pour la réception d'emails" setting_mail_handler_api_key: Clé de protection de l'API setting_sequential_project_identifiers: Générer des identifiants de projet séquentiels @@ -459,6 +472,8 @@ fr: setting_sys_api_key: Clé de protection de l'API setting_lost_password: Autoriser la réinitialisation par email de mot de passe perdu setting_new_item_menu_tab: Onglet de création d'objets dans le menu du project + setting_commit_logs_formatting: Appliquer le formattage de texte aux messages de commit + setting_timelog_required_fields: Champs obligatoire pour les temps passés permission_add_project: Créer un projet permission_add_subprojects: Créer des sous-projets @@ -494,6 +509,7 @@ fr: permission_view_time_entries: Voir le temps passé permission_edit_time_entries: Modifier les temps passés permission_edit_own_time_entries: Modifier son propre temps passé + permission_view_news: Voir les annonces permission_manage_news: Gérer les annonces permission_comment_news: Commenter les annonces permission_view_documents: Voir les documents @@ -601,7 +617,6 @@ fr: label_my_page: Ma page label_my_account: Mon compte label_my_projects: Mes projets - label_my_page_block: Blocs disponibles label_administration: Administration label_login: Connexion label_logout: Déconnexion @@ -698,7 +713,6 @@ fr: label_internal: Interne label_last_changes: "%{count} derniers changements" label_change_view_all: Voir tous les changements - label_personalize_page: Personnaliser cette page label_comment: Commentaire label_comment_plural: Commentaires label_x_comments: @@ -868,8 +882,6 @@ fr: label_user_mail_option_selected: "Pour tous les événements des projets sélectionnés..." label_user_mail_option_none: Aucune notification label_user_mail_option_only_my_events: Seulement pour ce que je surveille - label_user_mail_option_only_assigned: Seulement pour ce qui m'est assigné - label_user_mail_option_only_owner: Seulement pour ce que j'ai créé label_user_mail_no_self_notified: "Je ne veux pas être notifié des changements que j'effectue" label_registration_activation_by_email: activation du compte par email label_registration_manual_activation: activation manuelle du compte @@ -878,7 +890,6 @@ fr: label_age: Âge label_change_properties: Changer les propriétés label_general: Général - label_more: Plus label_scm: SCM label_plugins: Plugins label_ldap_authentication: Authentification LDAP @@ -888,7 +899,6 @@ fr: label_preferences: Préférences label_chronological_order: Dans l'ordre chronologique label_reverse_chronological_order: Dans l'ordre chronologique inverse - label_planning: Planning label_incoming_emails: Emails entrants label_generate_key: Générer une clé label_issue_watchers: Observateurs @@ -951,6 +961,7 @@ fr: label_attribute_of_assigned_to: "%{name} de l'assigné" label_attribute_of_user: "%{name} de l'utilisateur" label_attribute_of_fixed_version: "%{name} de la version cible" + label_attribute_of_object: "%{name} de \"%{object_name}\"" label_cross_project_descendants: Avec les sous-projets label_cross_project_tree: Avec tout l'arbre label_cross_project_hierarchy: Avec toute la hiérarchie @@ -1008,6 +1019,11 @@ fr: label_relations: Relations label_new_project_issue_tab_enabled: Afficher l'onglet "Nouvelle demande" label_new_object_tab_enabled: Afficher le menu déroulant "+" + label_table_of_contents: Contenu + label_font_default: Police par défaut + label_font_monospace: Police non proportionnelle + label_font_proportional: Police proportionnelle + label_last_notes: Dernières notes button_login: Connexion button_submit: Soumettre @@ -1197,10 +1213,6 @@ fr: description_all_columns: Toutes les colonnes description_issue_category_reassign: Choisir une catégorie description_wiki_subpages_reassign: Choisir une nouvelle page parent - description_date_range_list: Choisir une période prédéfinie - description_date_range_interval: Choisir une période - description_date_from: Date de début - description_date_to: Date de fin text_repository_identifier_info: 'Seuls les lettres minuscules (a-z), chiffres, tirets et tirets bas sont autorisés.
    Un fois sauvegardé, l''identifiant ne pourra plus être modifié.' label_parent_task_attributes_derived: Calculé à partir des sous-tâches label_parent_task_attributes_independent: Indépendent des sous-tâches @@ -1212,4 +1224,8 @@ fr: mail_body_security_notification_notify_enabled: Les notifications ont été activées pour l'adresse %{value} mail_body_security_notification_notify_disabled: Les notifications ont été désactivées pour l'adresse %{value} field_remote_ip: Adresse IP - label_no_preview: No preview available + label_no_preview: Aucun aperçu disponible + label_no_preview_alternative_html: Aucun aperçu disponible. Veuillez %{link} le fichier. + label_no_preview_download: télécharger + label_user_mail_option_only_assigned: Only for things I watch or I am assigned to + label_user_mail_option_only_owner: Only for things I watch or I am the owner of diff --git a/config/locales/gl.yml b/config/locales/gl.yml index 76813264f..b5de3acf6 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -159,6 +159,8 @@ gl: circular_dependency: "Esta relación podería crear unha dependencia circular" cant_link_an_issue_with_a_descendant: "As peticións non poden estar ligadas coas súas subtarefas" earlier_than_minimum_start_date: "Non pode ser antes de %{date} por mor de peticións anteriores" + 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" actionview_instancetag_blank_option: Por favor seleccione @@ -492,7 +494,6 @@ gl: label_module_plural: Módulos label_month: Mes label_months_from: meses de - label_more: Máis label_more_than_ago: "hai máis de" label_my_account: "Conta" label_my_page: "Páxina persoal" @@ -521,8 +522,6 @@ gl: label_password_lost: "Esqueceu o contrasinal?" label_permissions: Permisos label_permissions_report: Informe de permisos - label_personalize_page: Personalizar esta páxina - label_planning: Planificación label_please_login: "Acceder" label_plugins: Complementos label_precedes: anterior a @@ -910,7 +909,6 @@ gl: error_can_not_remove_role: "Este rol non pode eliminarse porque está a usarse." error_can_not_delete_tracker: "Este tipo de petición non pode eliminarse porque existen peticións deste tipo." field_principal: "Principal" - label_my_page_block: "Trebellos:" notice_failed_to_save_members: "Non foi posíbel gardar os membros: %{errors}." text_zoom_out: "Afastar" text_zoom_in: "Achegar" @@ -921,10 +919,8 @@ gl: project_module_calendar: "Calendario" button_edit_associated_wikipage: "Editar a páxina do wiki asociada: %{page_title}" field_text: "Campo de texto" - label_user_mail_option_only_owner: "Só para as cousas das que son o propietario" setting_default_notification_option: "Opción de notificación predeterminada" label_user_mail_option_only_my_events: "Só para as cousas que sigo ou nas que estou involucrado" - label_user_mail_option_only_assigned: "Só para cousas que teño asignadas" label_user_mail_option_none: "Non hai eventos." field_member_of_group: "Grupo do asignado" field_assigned_to_role: "Rol do asignado" @@ -981,16 +977,12 @@ gl: description_project_scope: "Ámbito da busca" description_filter: "Filtro" description_user_mail_notification: "Configuración das notificacións por correo electrónico" - description_date_from: "Introduza a data de inicio" description_message_content: "Contido da mensaxe" description_available_columns: "Columnas dispoñíbeis" - description_date_range_interval: "Escolla un intervalo seleccionando unha data de inicio e outra de fin." description_issue_category_reassign: "Escolla a categoría da petición." description_search: "Campo de busca" description_notes: "Notas" - description_date_range_list: "Escolla un intervalo da lista." description_choose_project: "Proxectos" - description_date_to: "Introduza a data de fin" description_query_sort_criteria_attribute: "Ordenar polo atributo" description_wiki_subpages_reassign: "Escoller unha nova páxina superior" description_selected_columns: "Columnas seleccionadas" @@ -1129,92 +1121,118 @@ gl: setting_link_copied_issue: "Ligar aos tíckets ao copialos" label_link_copied_issue: "Ligar ao tícket copiado" label_ask: "Preguntar" - label_search_attachments_yes: Search attachment filenames and descriptions - label_search_attachments_no: Do not search attachments - label_search_attachments_only: Search attachments only - label_search_open_issues_only: Open issues only + label_search_attachments_yes: Buscar nomes e descricións dos ficheiros + label_search_attachments_no: Non buscar ficheiros + label_search_attachments_only: Buscar só ficheiros + label_search_open_issues_only: Só peticións abertas field_address: Correo electrónico - setting_max_additional_emails: Maximum number of additional email addresses - label_email_address_plural: Emails - label_email_address_add: Add email address - label_enable_notifications: Enable notifications - label_disable_notifications: Disable notifications - setting_search_results_per_page: Search results per page - label_blank_value: blank - permission_copy_issues: Copy issues - error_password_expired: Your password has expired or the administrator requires you - to change it. - field_time_entries_visibility: Time logs visibility - setting_password_max_age: Require password change after - label_parent_task_attributes: Parent tasks attributes - label_parent_task_attributes_derived: Calculated from subtasks - label_parent_task_attributes_independent: Independent of subtasks - label_time_entries_visibility_all: All time entries - label_time_entries_visibility_own: Time entries created by the user - label_member_management: Member management - label_member_management_all_roles: All roles - label_member_management_selected_roles_only: Only these roles - label_password_required: Confirm your password to continue + setting_max_additional_emails: Máximo número de enderezos de correo adicionais + label_email_address_plural: Correos + label_email_address_add: Engadir enderezo de correo + label_enable_notifications: Activar notificacións + label_disable_notifications: Desactivar notificacións + setting_search_results_per_page: Resultados da busca por páxina + label_blank_value: En branco + permission_copy_issues: Copiar peticións + error_password_expired: A túa contrasinal caducou ou o administrador obrígate + a cambiala. + field_time_entries_visibility: Visibilidade das entradas de tempo + setting_password_max_age: Obrigar a cambiar a contrasinal despois de + label_parent_task_attributes: Atributos da tarefa pai + label_parent_task_attributes_derived: Calculada a partir das subtarefas + label_parent_task_attributes_independent: Independente das subtarefas + label_time_entries_visibility_all: Todas as entradas de tempo + label_time_entries_visibility_own: Horas creadas polo usuario + label_member_management: Xestión de membros + label_member_management_all_roles: Todos os roles + label_member_management_selected_roles_only: Só estes roles + label_password_required: Confirma a túa contrasinal para continuar label_total_spent_time: "Tempo total empregado" - notice_import_finished: "%{count} items have been imported" - notice_import_finished_with_errors: "%{count} out of %{total} items could not be imported" - error_invalid_file_encoding: The file is not a valid %{encoding} encoded file - error_invalid_csv_file_or_settings: The file is not a CSV file or does not match the - settings below - error_can_not_read_import_file: An error occurred while reading the file to import - permission_import_issues: Import issues - label_import_issues: Import issues - label_select_file_to_import: Select the file to import - label_fields_separator: Field separator - label_fields_wrapper: Field wrapper - label_encoding: Encoding - label_comma_char: Comma - label_semi_colon_char: Semicolon - label_quote_char: Quote - label_double_quote_char: Double quote - label_fields_mapping: Fields mapping - label_file_content_preview: File content preview - label_create_missing_values: Create missing values - button_import: Import - field_total_estimated_hours: Total estimated time + notice_import_finished: "%{count} elementos foron importados" + notice_import_finished_with_errors: "%{count} dun total de %{total} elementos non puideron ser importados" + error_invalid_file_encoding: O ficheiro non é un ficheiro codificado %{encoding} válido + error_invalid_csv_file_or_settings: O ficheiro non é un arquivo CSV ou non coincide coas + opcións de abaixo + error_can_not_read_import_file: Aconteceu un erro lendo o ficheiro a importar + permission_import_issues: Importar peticións + label_import_issues: Importar peticións + label_select_file_to_import: Selecciona o ficheiro a importar + label_fields_separator: Separador dos campos + label_fields_wrapper: Envoltorio dos campos + label_encoding: Codificación + label_comma_char: Coma + label_semi_colon_char: Punto e coma + label_quote_char: Comilla simple + label_double_quote_char: Comilla dobre + label_fields_mapping: Mapeo de campos + label_file_content_preview: Vista previa do contido + label_create_missing_values: Crear valores non presentes + button_import: Importar + field_total_estimated_hours: Total de tempo estimado label_api: API - label_total_plural: Totals - label_assigned_issues: Assigned issues - label_field_format_enumeration: Key/value list + label_total_plural: Totais + label_assigned_issues: Peticións asignadas + label_field_format_enumeration: Listaxe chave/valor label_f_hour_short: '%{value} h' - field_default_version: Default version - error_attachment_extension_not_allowed: Attachment extension %{extension} is not allowed - setting_attachment_extensions_allowed: Allowed extensions - setting_attachment_extensions_denied: Disallowed extensions - label_any_open_issues: any open issues - label_no_open_issues: no open issues - label_default_values_for_new_users: Default values for new users - error_ldap_bind_credentials: Invalid LDAP Account/Password + field_default_version: Versión predeterminada + error_attachment_extension_not_allowed: A extensión anexada %{extension} non é permitida + setting_attachment_extensions_allowed: Extensións permitidas + setting_attachment_extensions_denied: Extensións prohibidas + label_any_open_issues: Calquera petición aberta + label_no_open_issues: Peticións non abertas + label_default_values_for_new_users: Valor predeterminado para novos usuarios + error_ldap_bind_credentials: A conta/contrasinal do LDAP non é válida setting_sys_api_key: Chave da API - setting_lost_password: "Esqueceu o contrasinal?" - mail_subject_security_notification: Security notification - mail_body_security_notification_change: ! '%{field} was changed.' - mail_body_security_notification_change_to: ! '%{field} was changed to %{value}.' - mail_body_security_notification_add: ! '%{field} %{value} was added.' - mail_body_security_notification_remove: ! '%{field} %{value} was removed.' - mail_body_security_notification_notify_enabled: Email address %{value} now receives - notifications. - mail_body_security_notification_notify_disabled: Email address %{value} no longer - receives notifications. - mail_body_settings_updated: ! 'The following settings were changed:' - field_remote_ip: IP address - label_wiki_page_new: New wiki page - label_relations: Relations - button_filter: Filter - mail_body_password_updated: Your password has been changed. - label_no_preview: No preview available - error_no_tracker_allowed_for_new_issue_in_project: The project doesn't have any trackers - for which you can create an issue - label_tracker_all: All trackers - label_new_project_issue_tab_enabled: Display the "New issue" tab - setting_new_item_menu_tab: Project menu tab for creating new objects - label_new_object_tab_enabled: Display the "+" drop-down - error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers - for which you can create an issue - error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot - be reassigned to an issue that is about to be deleted + setting_lost_password: Esqueceu a contrasinal? + mail_subject_security_notification: Notificación de seguridade + mail_body_security_notification_change: ! '%{field} modificado.' + mail_body_security_notification_change_to: ! '%{field} modificado por %{value}.' + mail_body_security_notification_add: ! '%{field} %{value} engadido.' + mail_body_security_notification_remove: ! '%{field} %{value} eliminado.' + mail_body_security_notification_notify_enabled: O correo electrónico %{value} agora recibirá + notificacións + mail_body_security_notification_notify_disabled: O correo electrónico %{value} deixará de recibir + notificacións + mail_body_settings_updated: ! 'As seguintes opcións foron actualizadas:' + field_remote_ip: Enderezo IP + label_wiki_page_new: Nova páxina wiki + label_relations: Relacións + button_filter: Filtro + mail_body_password_updated: A súa contrasinal foi cambiada. + label_no_preview: Non hai vista previa dispoñible + error_no_tracker_allowed_for_new_issue_in_project: O proxecto non ten ningún tipo + para que poida crear unha petición + label_tracker_all: Tódolos tipos + label_new_project_issue_tab_enabled: Amosar a lapela "Nova petición" + setting_new_item_menu_tab: Lapela no menú de cada proxecto para creación de novos obxectos + label_new_object_tab_enabled: Amosar a lista despregable "+" + error_no_projects_with_tracker_allowed_for_new_issue: Non hai proxectos con tipos + para as que poida crear unha petición + field_textarea_font: Fonte usada nas áreas de texto + label_font_default: Fonte por defecto + label_font_monospace: Fonte Monospaced + label_font_proportional: Fonte Proporcional + setting_timespan_format: Formato de período temporal + label_table_of_contents: Índice de contidos + setting_commit_logs_formatting: Aplicar formateo de texto para as mensaxes de commit + setting_mail_handler_enable_regex_delimiters: Activar expresións regulares + error_move_of_child_not_possible: 'A subtarefa %{child} non se pode mover ao novo + proxecto: %{errors}' + error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: O tempo empregado non pode + ser reasignado a unha petición que vai ser eliminada + setting_timelog_required_fields: Campos obrigatorios para as imputacións de tempo + label_attribute_of_object: '%{object_name}''s %{name}' + label_user_mail_option_only_assigned: Só para as cousas que sigo ou que teño asignado + label_user_mail_option_only_owner: Só para as cousas que sigo ou son o propietario + warning_fields_cleared_on_bulk_edit: Os cambios provocarán o borrado automático + dos valores de un ou máis campos dos obxectos seleccionados + field_updated_by: Actualizado por + field_last_updated_by: Última actualización por + field_full_width_layout: Deseño para ancho completo + label_last_notes: Últimas notas + field_digest: Suma de verificación + field_default_assigned_to: Asignado por defecto + setting_show_custom_fields_on_registration: Amosar os campos personalizados no rexistro + permission_view_news: Ver noticias + label_no_preview_alternative_html: Non hai vista previa dispoñible. %{link} o ficheiro no seu lugar. + label_no_preview_download: Descargar diff --git a/config/locales/he.yml b/config/locales/he.yml index 5be5d040b..59d70d834 100644 --- a/config/locales/he.yml +++ b/config/locales/he.yml @@ -135,6 +135,8 @@ he: circular_dependency: "קשר זה יצור תלות מעגלית" cant_link_an_issue_with_a_descendant: "לא ניתן לקשר נושא לתת־משימה שלו" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" actionview_instancetag_blank_option: בחר בבקשה @@ -485,7 +487,6 @@ he: label_my_page: הדף שלי label_my_account: החשבון שלי label_my_projects: הפרויקטים שלי - label_my_page_block: בלוק הדף שלי label_administration: ניהול label_login: התחבר label_logout: התנתק @@ -573,7 +574,6 @@ he: label_internal: פנימי label_last_changes: "%{count} שינוים אחרונים" label_change_view_all: צפה בכל השינוים - label_personalize_page: התאם אישית דף זה label_comment: תגובה label_comment_plural: תגובות label_x_comments: @@ -724,8 +724,6 @@ he: label_user_mail_option_all: "לכל אירוע בכל הפרויקטים שלי" label_user_mail_option_selected: "לכל אירוע בפרויקטים שבחרתי בלבד..." label_user_mail_option_only_my_events: עבור דברים שאני צופה או מעורב בהם בלבד - label_user_mail_option_only_assigned: עבור דברים שאני אחראי עליהם בלבד - label_user_mail_option_only_owner: עבור דברים שאני הבעלים שלהם בלבד label_user_mail_no_self_notified: "אני לא רוצה שיודיעו לי על שינויים שאני מבצע" label_registration_activation_by_email: הפעל חשבון באמצעות דוא"ל label_registration_manual_activation: הפעלת חשבון ידנית @@ -734,7 +732,6 @@ he: label_age: גיל label_change_properties: שנה מאפיינים label_general: כללי - label_more: עוד label_scm: מערכת ניהול תצורה label_plugins: תוספים label_ldap_authentication: הזדהות LDAP @@ -744,7 +741,6 @@ he: label_preferences: העדפות label_chronological_order: בסדר כרונולוגי label_reverse_chronological_order: בסדר כרונולוגי הפוך - label_planning: תכנון label_incoming_emails: דוא"ל נכנס label_generate_key: צור מפתח label_issue_watchers: צופים @@ -972,16 +968,12 @@ he: description_project_scope: Search scope description_filter: Filter description_user_mail_notification: Mail notification settings - description_date_from: Enter start date description_message_content: Message content description_available_columns: Available Columns - description_date_range_interval: Choose range by selecting start and end date description_issue_category_reassign: Choose issue category description_search: Searchfield description_notes: Notes - description_date_range_list: Choose range from list description_choose_project: Projects - description_date_to: Enter end date description_query_sort_criteria_attribute: Sort attribute description_wiki_subpages_reassign: Choose new parent page description_selected_columns: Selected Columns @@ -1213,5 +1205,31 @@ he: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/hr.yml b/config/locales/hr.yml index 19a8a25b6..89ea2991d 100644 --- a/config/locales/hr.yml +++ b/config/locales/hr.yml @@ -125,6 +125,8 @@ hr: circular_dependency: "Ovaj relacija stvara kružnu ovisnost" cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" actionview_instancetag_blank_option: Molimo odaberite @@ -542,7 +544,6 @@ hr: label_internal: Interno label_last_changes: "Posljednjih %{count} promjena" label_change_view_all: Prikaz svih promjena - label_personalize_page: Prilagodite ovu stranicu label_comment: Komentar label_comment_plural: Komentari label_x_comments: @@ -697,7 +698,6 @@ hr: label_age: Starost label_change_properties: Promijeni svojstva label_general: Općenito - label_more: Još label_scm: SCM label_plugins: Plugins label_ldap_authentication: LDAP autentikacija @@ -707,7 +707,6 @@ hr: label_preferences: Preferences label_chronological_order: U kronološkom redoslijedu label_reverse_chronological_order: U obrnutom kronološkom redoslijedu - label_planning: Planiranje label_incoming_emails: Dolazne poruke e-pošte label_generate_key: Generiraj ključ label_issue_watchers: Promatrači @@ -895,7 +894,6 @@ hr: error_can_not_remove_role: This role is in use and can not be deleted. error_can_not_delete_tracker: This tracker contains issues and cannot be deleted. field_principal: Principal - label_my_page_block: My page block notice_failed_to_save_members: "Failed to save member(s): %{errors}." text_zoom_out: Zoom out text_zoom_in: Zoom in @@ -906,10 +904,8 @@ hr: project_module_calendar: Calendar button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}" field_text: Text field - label_user_mail_option_only_owner: Only for things I am the owner of setting_default_notification_option: Default notification option label_user_mail_option_only_my_events: Only for things I watch or I'm involved in - label_user_mail_option_only_assigned: Only for things I am assigned to label_user_mail_option_none: No events field_member_of_group: Assignee's group field_assigned_to_role: Assignee's role @@ -966,16 +962,12 @@ hr: description_project_scope: Search scope description_filter: Filter description_user_mail_notification: Mail notification settings - description_date_from: Enter start date description_message_content: Message content description_available_columns: Available Columns - description_date_range_interval: Choose range by selecting start and end date description_issue_category_reassign: Choose issue category description_search: Searchfield description_notes: Notes - description_date_range_list: Choose range from list description_choose_project: Projects - description_date_to: Enter end date description_query_sort_criteria_attribute: Sort attribute description_wiki_subpages_reassign: Choose new parent page description_selected_columns: Selected Columns @@ -1207,5 +1199,31 @@ hr: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/hu.yml b/config/locales/hu.yml index 676ce3842..37ff35a33 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -151,6 +151,8 @@ circular_dependency: "Ez a kapcsolat egy körkörös függőséget eredményez" cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" actionview_instancetag_blank_option: Kérem válasszon @@ -469,7 +471,6 @@ label_internal: Belső label_last_changes: "utolsó %{count} változás" label_change_view_all: Minden változás megtekintése - label_personalize_page: Az oldal testreszabása label_comment: Megjegyzés label_comment_plural: Megjegyzés label_x_comments: @@ -612,7 +613,6 @@ label_age: Kor label_change_properties: Tulajdonságok változtatása label_general: Általános - label_more: továbbiak label_scm: SCM label_plugins: Pluginek label_ldap_authentication: LDAP azonosítás @@ -622,7 +622,6 @@ label_preferences: Tulajdonságok label_chronological_order: Időrendben label_reverse_chronological_order: Fordított időrendben - label_planning: Tervezés button_login: Bejelentkezés button_submit: Elfogad @@ -915,7 +914,6 @@ error_can_not_delete_tracker: Ebbe a kategóriába feladatok tartoznak és ezért nem törölhető. label_project_copy_notifications: Küldjön e-mail értesítéseket projektmásolás közben. field_principal: Felelős - label_my_page_block: Saját kezdőlap-blokk notice_failed_to_save_members: "Nem sikerült menteni a tago(ka)t: %{errors}." text_zoom_out: Kicsinyít text_zoom_in: Nagyít @@ -926,10 +924,8 @@ project_module_calendar: Naptár button_edit_associated_wikipage: "Hozzárendelt Wiki oldal szerkesztése: %{page_title}" field_text: Szöveg mező - label_user_mail_option_only_owner: Csak arról, aminek én vagyok a tulajdonosa setting_default_notification_option: Alapértelmezett értesítési beállítások label_user_mail_option_only_my_events: Csak az általam megfigyelt dolgokról vagy amiben részt veszek - label_user_mail_option_only_assigned: Csak a hozzámrendelt dolgokról label_user_mail_option_none: Semilyen eseményről field_member_of_group: Hozzárendelt csoport field_assigned_to_role: Hozzárendelt szerepkör @@ -987,16 +983,12 @@ description_project_scope: Search scope description_filter: Filter description_user_mail_notification: Mail notification settings - description_date_from: Enter start date description_message_content: Message content description_available_columns: Available Columns - description_date_range_interval: Choose range by selecting start and end date description_issue_category_reassign: Choose issue category description_search: Searchfield description_notes: Notes - description_date_range_list: Choose range from list description_choose_project: Projects - description_date_to: Enter end date description_query_sort_criteria_attribute: Sort attribute description_wiki_subpages_reassign: Choose new parent page description_selected_columns: Selected Columns @@ -1227,5 +1219,31 @@ label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/id.yml b/config/locales/id.yml index e9dffd7bc..1ae8dbc97 100644 --- a/config/locales/id.yml +++ b/config/locales/id.yml @@ -130,6 +130,8 @@ id: circular_dependency: "kaitan ini akan menghasilkan circular dependency" cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" actionview_instancetag_blank_option: Silakan pilih @@ -534,7 +536,6 @@ id: label_internal: Internal label_last_changes: "%{count} perubahan terakhir" label_change_view_all: Tampilkan semua perubahan - label_personalize_page: Personalkan halaman ini label_comment: Komentar label_comment_plural: Komentar label_x_comments: @@ -688,7 +689,6 @@ id: label_age: Umur label_change_properties: Rincian perubahan label_general: Umum - label_more: Lanjut label_scm: SCM label_plugins: Plugin label_ldap_authentication: Otentikasi LDAP @@ -698,7 +698,6 @@ id: label_preferences: Preferensi label_chronological_order: Urut sesuai kronologis label_reverse_chronological_order: Urut dari yang terbaru - label_planning: Perencanaan label_incoming_emails: Email masuk label_generate_key: Buat kunci label_issue_watchers: Pemantau @@ -900,7 +899,6 @@ id: error_can_not_remove_role: This role is in use and can not be deleted. error_can_not_delete_tracker: This tracker contains issues and cannot be deleted. field_principal: Principal - label_my_page_block: My page block notice_failed_to_save_members: "Failed to save member(s): %{errors}." text_zoom_out: Zoom out text_zoom_in: Zoom in @@ -911,10 +909,8 @@ id: project_module_calendar: Calendar button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}" field_text: Text field - label_user_mail_option_only_owner: Only for things I am the owner of setting_default_notification_option: Default notification option label_user_mail_option_only_my_events: Only for things I watch or I'm involved in - label_user_mail_option_only_assigned: Only for things I am assigned to label_user_mail_option_none: No events field_member_of_group: Assignee's group field_assigned_to_role: Assignee's role @@ -971,16 +967,12 @@ id: description_project_scope: Search scope description_filter: Filter description_user_mail_notification: Mail notification settings - description_date_from: Enter start date description_message_content: Message content description_available_columns: Available Columns - description_date_range_interval: Choose range by selecting start and end date description_issue_category_reassign: Choose issue category description_search: Searchfield description_notes: Notes - description_date_range_list: Choose range from list description_choose_project: Projects - description_date_to: Enter end date description_query_sort_criteria_attribute: Sort attribute description_wiki_subpages_reassign: Choose new parent page description_selected_columns: Selected Columns @@ -1212,5 +1204,31 @@ id: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/it.yml b/config/locales/it.yml index 1193a173e..97bf67f14 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -135,6 +135,8 @@ it: circular_dependency: "Questa relazione creerebbe una dipendenza circolare" cant_link_an_issue_with_a_descendant: "Una segnalazione non può essere collegata a una delle sue discendenti" earlier_than_minimum_start_date: "non può essere precedente a %{date} a causa di una precedente segnalazione" + 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" actionview_instancetag_blank_option: Scegli @@ -407,7 +409,6 @@ it: label_internal: Interno label_last_changes: "ultime %{count} modifiche" label_change_view_all: Tutte le modifiche - label_personalize_page: Personalizza la pagina label_comment: Commento label_comment_plural: Commenti label_x_comments: @@ -657,7 +658,6 @@ it: label_associated_revisions: Revisioni associate setting_user_format: Formato visualizzazione utenti text_status_changed_by_changeset: "Applicata nel changeset %{value}." - label_more: Altro text_issues_destroy_confirmation: 'Sei sicuro di voler eliminare le segnalazioni selezionate?' label_scm: SCM text_select_project_modules: 'Seleziona i moduli abilitati per questo progetto:' @@ -706,7 +706,6 @@ it: label_overall_activity: Attività generale setting_default_projects_public: I nuovi progetti sono pubblici in modo predefinito error_scm_annotate: "L'oggetto non esiste o non può essere annotato." - label_planning: Pianificazione text_subprojects_destroy_warning: "Anche i suoi sottoprogetti: %{value} verranno eliminati." label_and_its_subprojects: "%{value} ed i suoi sottoprogetti" mail_body_reminder: "%{count} segnalazioni che ti sono state assegnate scadranno nei prossimi %{days} giorni:" @@ -896,7 +895,6 @@ it: error_can_not_remove_role: Questo ruolo è in uso e non può essere eliminato. error_can_not_delete_tracker: Questo tracker contiene segnalazioni e non può essere eliminato. field_principal: Principale - label_my_page_block: La mia pagina di blocco notice_failed_to_save_members: "Impossibile salvare il membro(i): %{errors}." text_zoom_out: Riduci ingrandimento text_zoom_in: Aumenta ingrandimento @@ -907,10 +905,8 @@ it: project_module_calendar: Calendario button_edit_associated_wikipage: "Modifica la pagina wiki associata: %{page_title}" field_text: Campo di testo - label_user_mail_option_only_owner: Solo se io sono il proprietario setting_default_notification_option: Opzione di notifica predefinita label_user_mail_option_only_my_events: Solo se sono un osservatore o sono coinvolto - label_user_mail_option_only_assigned: Solo quando mi assegnano attività label_user_mail_option_none: Nessun evento field_member_of_group: Gruppo dell'assegnatario field_assigned_to_role: Ruolo dell'assegnatario @@ -970,16 +966,12 @@ it: description_project_scope: Ambito della ricerca description_filter: Filtro description_user_mail_notification: Impostazioni notifica via mail - description_date_from: Inserisci la data d'inizio description_message_content: Contenuto del messaggio description_available_columns: Colonne disponibili - description_date_range_interval: Scegli l'intervallo selezionando la data di inizio e di fine description_issue_category_reassign: Scegli la categoria della segnalazione description_search: Campo di ricerca description_notes: Note - description_date_range_list: Scegli l'intervallo dalla lista description_choose_project: Progetti - description_date_to: Inserisci la data di fine description_query_sort_criteria_attribute: Attributo di ordinamento description_wiki_subpages_reassign: Scegli la nuova pagina padre description_selected_columns: Colonne selezionate @@ -1203,5 +1195,31 @@ it: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/ja.yml b/config/locales/ja.yml index f3ee1d848..30b2f5c26 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -151,6 +151,8 @@ ja: circular_dependency: "この関係では、循環依存になります" cant_link_an_issue_with_a_descendant: "親子関係にあるチケット間での関連の設定はできません" earlier_than_minimum_start_date: "を%{date}より前にすることはできません。先行するチケットがあります" + not_a_regexp: "は正しい正規表現ではありません" + open_issue_with_closed_parent: "完了したチケットに未完了の子チケットを追加することはできません" actionview_instancetag_blank_option: 選んでください @@ -195,7 +197,7 @@ ja: notice_unable_delete_version: バージョンを削除できません notice_unable_delete_time_entry: 作業時間を削除できません notice_issue_done_ratios_updated: チケットの進捗率を更新しました。 - notice_gantt_chart_truncated: ガントチャートは、最大表示項目数(%{max})を超えたため切り捨てられました。 + notice_gantt_chart_truncated: ガントチャートは、最大表示件数(%{max})を超えたため切り捨てられました。 error_can_t_load_default_data: "デフォルト設定がロードできませんでした: %{value}" error_scm_not_found: リポジトリに、エントリ/リビジョンが存在しません。 @@ -246,7 +248,7 @@ ja: field_author: 作成者 field_created_on: 作成日 field_updated_on: 更新日 - field_field_format: 書式 + field_field_format: 形式 field_is_for_all: 全プロジェクト向け field_possible_values: 選択肢 field_regexp: 正規表現 @@ -288,11 +290,11 @@ ja: field_host: ホスト field_port: ポート field_account: アカウント - field_base_dn: 検索範囲 - field_attr_login: ログイン名属性 - field_attr_firstname: 名前属性 - field_attr_lastname: 苗字属性 - field_attr_mail: メール属性 + field_base_dn: ベースDN + field_attr_login: ログインIDの属性 + field_attr_firstname: 名の属性 + field_attr_lastname: 姓の属性 + field_attr_mail: メールアドレスの属性 field_onthefly: あわせてユーザーを作成 field_start_date: 開始日 field_done_ratio: 進捗率 @@ -350,10 +352,10 @@ ja: setting_bcc_recipients: 宛先を非表示(bcc) setting_plain_text_mail: プレインテキスト形式(HTMLなし) setting_host_name: ホスト名とパス - setting_text_formatting: テキストの書式 - setting_cache_formatted_text: 書式化されたテキストをキャッシュする + setting_text_formatting: テキスト書式 + setting_cache_formatted_text: テキスト書式の変換結果をキャッシュ setting_wiki_compression: Wiki履歴を圧縮する - setting_feeds_limit: Atomフィードの項目数の上限 + setting_feeds_limit: Atomフィードの最大出力件数 setting_default_projects_public: デフォルトで新しいプロジェクトは公開にする setting_autofetch_changesets: コミットを自動取得する setting_sys_api_enabled: リポジトリ管理用のWebサービスを有効にする @@ -369,7 +371,7 @@ ja: setting_emails_footer: メールのフッタ setting_protocol: プロトコル setting_per_page_options: ページごとの表示件数 - setting_user_format: ユーザー名の表示書式 + setting_user_format: ユーザー名の表示形式 setting_activity_days_default: プロジェクトの活動ページに表示される日数 setting_display_subprojects_issues: サブプロジェクトのチケットをメインプロジェクトに表示する setting_enabled_scm: 使用するバージョン管理システム @@ -394,7 +396,7 @@ ja: setting_default_notification_option: デフォルトのメール通知オプション setting_commit_logtime_enabled: コミット時に作業時間を記録する setting_commit_logtime_activity_id: 作業時間の作業分類 - setting_gantt_items_limit: ガントチャート最大表示項目数 + setting_gantt_items_limit: ガントチャート最大表示件数 setting_default_projects_tracker_ids: 新規プロジェクトにおいてデフォルトで有効になるトラッカー permission_add_project: プロジェクトの追加 @@ -425,6 +427,7 @@ ja: permission_edit_time_entries: 作業時間の編集 permission_edit_own_time_entries: 自身が記入した作業時間の編集 permission_manage_project_activities: 作業分類 (時間管理) の管理 + permission_view_news: ニュースの閲覧 permission_manage_news: ニュースの管理 permission_comment_news: ニュースへのコメント permission_view_documents: 文書の閲覧 @@ -497,7 +500,7 @@ ja: label_member_plural: メンバー label_tracker: トラッカー label_tracker_plural: トラッカー - label_tracker_new: 新しいトラッカーを作成 + label_tracker_new: 新しいトラッカー label_workflow: ワークフロー label_issue_status: チケットのステータス label_issue_status_plural: チケットのステータス @@ -507,8 +510,8 @@ ja: label_issue_category_new: 新しいカテゴリ label_custom_field: カスタムフィールド label_custom_field_plural: カスタムフィールド - label_custom_field_new: 新しいカスタムフィールドを作成 - label_enumerations: 列挙項目 + label_custom_field_new: 新しいカスタムフィールド + label_enumerations: 選択肢の値 label_enumeration_new: 新しい値 label_information: 情報 label_information_plural: 情報 @@ -520,7 +523,6 @@ ja: label_my_page: マイページ label_my_account: 個人設定 label_my_projects: マイプロジェクト - label_my_page_block: マイページパーツ label_administration: 管理 label_login: ログイン label_logout: ログアウト @@ -609,7 +611,6 @@ ja: label_internal: 内部 label_last_changes: "最新の変更 %{count}件" label_change_view_all: すべての変更を表示 - label_personalize_page: このページをパーソナライズする label_comment: コメント label_comment_plural: コメント label_x_comments: @@ -698,7 +699,7 @@ ja: label_change_plural: 変更 label_statistics: 統計 label_commits_per_month: 月別のコミット - label_commits_per_author: 起票者別のコミット + label_commits_per_author: 作成者別のコミット label_diff: 差分 label_view_diff: 差分を表示 label_diff_inline: インライン @@ -762,9 +763,9 @@ ja: label_user_mail_option_all: "参加しているプロジェクトのすべての通知" label_user_mail_option_selected: "選択したプロジェクトのすべての通知..." label_user_mail_option_none: "通知しない" - label_user_mail_option_only_my_events: "ウォッチまたは関係している事柄のみ" - label_user_mail_option_only_assigned: "自分が担当している事柄のみ" - label_user_mail_option_only_owner: "自分が作成した事柄のみ" + label_user_mail_option_only_my_events: "ウォッチ中または自分が関係しているもの" + label_user_mail_option_only_assigned: "ウォッチ中または自分が担当しているもの" + label_user_mail_option_only_owner: "ウォッチ中または自分が作成したもの" label_user_mail_no_self_notified: 自分自身による変更の通知は不要 label_registration_activation_by_email: メールでアカウントを有効化 label_registration_manual_activation: 手動でアカウントを有効化 @@ -773,7 +774,6 @@ ja: label_age: 経過期間 label_change_properties: プロパティの変更 label_general: 全般 - label_more: 続き label_scm: バージョン管理システム label_plugins: プラグイン label_ldap_authentication: LDAP認証 @@ -783,7 +783,6 @@ ja: label_preferences: 設定 label_chronological_order: 古い順 label_reverse_chronological_order: 新しい順 - label_planning: 計画 label_incoming_emails: 受信メール label_generate_key: キーの生成 label_issue_watchers: ウォッチャー @@ -808,7 +807,7 @@ ja: label_copy_source: コピー元 label_copy_target: コピー先 label_copy_same_as_target: 同じコピー先 - label_display_used_statuses_only: このトラッカーで使われているステータスのみ表示する + label_display_used_statuses_only: このトラッカーで使用中のステータスのみ表示 label_api_access_key: APIアクセスキー label_missing_api_access_key: APIアクセスキーが見つかりません label_api_access_key_created_on: "APIアクセスキーは%{value}前に作成されました" @@ -939,7 +938,7 @@ ja: text_warn_on_leaving_unsaved: このページから移動すると、保存されていないデータが失われます。 text_scm_path_encoding_note: "デフォルト: UTF-8" text_mercurial_repository_note: "ローカルリポジトリ (例: /hgrepo, c:\\hgrepo)" - text_git_repository_note: "Bare、かつ、ローカルリポジトリ (例: /gitrepo, c:\\gitrepo)" + text_git_repository_note: "ローカルのベア(bare)リポジトリ (例: /gitrepo, c:\\gitrepo)" text_scm_command: コマンド text_scm_command_version: バージョン text_scm_config: バージョン管理システムのコマンドをconfig/configuration.ymlで設定できます。設定後、Redmineを再起動してください。 @@ -997,16 +996,12 @@ ja: description_project_scope: 検索範囲 description_filter: Filter description_user_mail_notification: メール通知の設定 - description_date_from: 開始日 description_message_content: 内容 description_available_columns: 利用できる項目 - description_date_range_interval: 日付で指定 description_issue_category_reassign: 新しいカテゴリを選択してください description_search: 検索キーワード description_notes: 注記 - description_date_range_list: 一覧から選択 description_choose_project: プロジェクト - description_date_to: 終了日 description_query_sort_criteria_attribute: 項目 description_wiki_subpages_reassign: 新しい親ページを選択してください description_selected_columns: 選択された項目 @@ -1163,8 +1158,8 @@ ja: label_member_management_selected_roles_only: 次のロールのみ label_password_required: この操作を続行するにはパスワードを入力してください label_total_spent_time: 合計作業時間 - notice_import_finished: "%{count}件の項目をすべてインポートしました" - notice_import_finished_with_errors: "全%{total}件中%{count}件の項目がインポートできませんでした" + notice_import_finished: "%{count}件のデータをすべてインポートしました" + notice_import_finished_with_errors: "全%{total}件中%{count}件のデータがインポートできませんでした" error_invalid_file_encoding: このファイルのエンコーディングは正しい%{encoding}ではありません error_invalid_csv_file_or_settings: このファイルはCSVファイルではないか、以下の設定と一致していません error_can_not_read_import_file: インポート元のファイルを読み込み中にエラーが発生しました @@ -1218,4 +1213,25 @@ ja: setting_new_item_menu_tab: 新規オブジェクト作成タブ label_new_object_tab_enabled: '"+" ドロップダウンを表示' error_no_projects_with_tracker_allowed_for_new_issue: チケットを追加できるプロジェクトがありません + field_textarea_font: テキストエリアのフォント + label_font_default: デフォルト + label_font_monospace: 等幅 + label_font_proportional: プロポーショナル + setting_timespan_format: 時間の形式 + label_table_of_contents: 目次 + setting_commit_logs_formatting: コミットメッセージにテキスト書式を適用 + setting_mail_handler_enable_regex_delimiters: 正規表現を使用 + error_move_of_child_not_possible: '子チケット %{child} を指定されたプロジェクトに移動できませんでした: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: 削除対象チケットへの作業時間の再割り当てはできません + setting_timelog_required_fields: 作業時間の必須入力フィールド + label_attribute_of_object: '%{object_name}の %{name}' + warning_fields_cleared_on_bulk_edit: この編集操作により次のフィールドの値がチケットから削除されます + field_updated_by: 更新者 + field_last_updated_by: 最終更新者 + field_full_width_layout: ワイド表示 + label_last_notes: 最新の注記 + field_digest: チェックサム + field_default_assigned_to: デフォルトの担当者 + setting_show_custom_fields_on_registration: アカウント登録画面でカスタムフィールドを表示 + label_no_preview_alternative_html: このファイルはプレビューできません。 %{link} してください。 + label_no_preview_download: ダウンロード diff --git a/config/locales/ko.yml b/config/locales/ko.yml index de4a22f10..a0473a759 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -177,6 +177,8 @@ ko: circular_dependency: "이 관계는 순환 의존관계를 만들 수 있습니다" cant_link_an_issue_with_a_descendant: "일감은 하위 일감과 연결할 수 없습니다." earlier_than_minimum_start_date: "시작날짜 %{date}보다 앞선 시간으로 설정할 수 없습니다." + 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" actionview_instancetag_blank_option: 선택하세요 @@ -572,7 +574,6 @@ ko: label_internal: 내부 label_last_changes: "최근 %{count}개의 변경사항" label_change_view_all: 모든 변경 내역 보기 - label_personalize_page: 입맛대로 구성하기 label_comment: 댓글 label_comment_plural: 댓글 label_x_comments: @@ -721,7 +722,6 @@ ko: label_age: 마지막 수정일 label_change_properties: 속성 변경 label_general: 일반 - label_more: 제목 및 설명 수정 label_scm: 형상관리시스템 label_plugins: 플러그인 label_ldap_authentication: LDAP 인증 @@ -731,7 +731,6 @@ ko: label_preferences: 설정 label_chronological_order: 시간 순으로 정렬 label_reverse_chronological_order: 시간 역순으로 정렬 - label_planning: 프로젝트계획 label_incoming_emails: 수신 메일 label_generate_key: 키 생성 label_issue_watchers: 일감관람자 @@ -944,7 +943,6 @@ ko: error_can_not_remove_role: 이 역할은 현재 사용 중이이서 삭제할 수 없습니다. error_can_not_delete_tracker: 이 유형의 일감들이 있어서 삭제할 수 없습니다. field_principal: 신원 - label_my_page_block: 내 페이지 출력화면 notice_failed_to_save_members: "%{errors}:구성원을 저장 중 실패하였습니다" text_zoom_out: 더 작게 text_zoom_in: 더 크게 @@ -955,10 +953,8 @@ ko: project_module_calendar: 달력 button_edit_associated_wikipage: "연관된 위키 페이지 %{page_title} 수정" field_text: 텍스트 영역 - label_user_mail_option_only_owner: 내가 저자인 사항만 setting_default_notification_option: 기본 알림 옵션 label_user_mail_option_only_my_events: 내가 지켜보거나 속해있는 사항만 - label_user_mail_option_only_assigned: 나에게 할당된 사항만 label_user_mail_option_none: 알림 없음 field_member_of_group: 할당된 사람의 그룹 field_assigned_to_role: 할당된 사람의 역할 @@ -1019,16 +1015,12 @@ ko: description_project_scope: "검색 범위" description_filter: "검색 조건" description_user_mail_notification: "메일 알림 설정" - description_date_from: "시작 날짜 입력" description_message_content: "메세지 내용" description_available_columns: "가능한 컬럼" - description_date_range_interval: "시작과 끝 날짜로 범위를 선택하십시오." description_issue_category_reassign: "일감 범주를 선택하십시오." description_search: "검색항목" description_notes: "덧글" - description_date_range_list: "목록에서 범위를 선택 하십시오." description_choose_project: "프로젝트" - description_date_to: "종료 날짜 입력" description_query_sort_criteria_attribute: "정렬 속성" description_wiki_subpages_reassign: "새로운 상위 페이지를 선택하십시오." description_selected_columns: "선택된 컬럼" @@ -1247,5 +1239,31 @@ ko: label_new_object_tab_enabled: 메뉴에 "+" 탭 표시 error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/lt.yml b/config/locales/lt.yml index 105ed9271..b4ab1f541 100644 --- a/config/locales/lt.yml +++ b/config/locales/lt.yml @@ -135,6 +135,8 @@ lt: circular_dependency: "Šis ryšys sukurtų ciklinę priklausomybę" cant_link_an_issue_with_a_descendant: "Darbas negali būti susietas su viena iš savo darbo dalių" earlier_than_minimum_start_date: "negali būti anksčiau už %{date} dėl ankstesnių darbų" + not_a_regexp: "neteisingas reguliarusis reiškinys" + open_issue_with_closed_parent: "Atviras darbas negali būti pridėtas prie uždarytos tėvinės užduoties" actionview_instancetag_blank_option: Prašom parinkti @@ -587,7 +589,6 @@ lt: label_my_page: Mano puslapis label_my_account: Mano paskyra label_my_projects: Mano projektai - label_my_page_block: Mano puslapio blokas label_administration: Administravimas label_login: Prisijungti label_logout: Atsijungti @@ -684,7 +685,6 @@ lt: label_internal: Vidinis label_last_changes: "paskutiniai %{count} pokyčiai(-ių)" label_change_view_all: Peržiūrėti visus pakeitimus - label_personalize_page: Suasmeninti šį puslapį label_comment: Komentaras label_comment_plural: Komentarai label_x_comments: @@ -853,8 +853,6 @@ lt: label_user_mail_option_selected: "Bet kokiam įvykiui tiktai pasirinktuose projektuose ..." label_user_mail_option_none: "Jokių įvykių" label_user_mail_option_only_my_events: "Tiktai dalykai, kuriuos stebiu arba esu įtrauktas" - label_user_mail_option_only_assigned: "Tiktai dalykai, kuriems esu priskirtas" - label_user_mail_option_only_owner: "Tiktai dalykai, kurių šeimininkas esu aš" label_user_mail_no_self_notified: "Nenoriu būti informuotas apie pakeitimus, kuriuos pats atlieku" label_registration_activation_by_email: paskyros aktyvacija per e-paštą label_registration_manual_activation: rankinė paskyros aktyvacija @@ -863,7 +861,6 @@ lt: label_age: Amžius label_change_properties: Pakeisti nustatymus label_general: Bendri(-as) - label_more: Daugiau label_scm: SCM label_plugins: Įskiepiai label_ldap_authentication: LDAP autentifikacija @@ -873,7 +870,6 @@ lt: label_preferences: Savybės label_chronological_order: Chronologine tvarka label_reverse_chronological_order: Atbuline chronologine tvarka - label_planning: Planavimas label_incoming_emails: Įeinantys laiškai label_generate_key: Generuoti raktą label_issue_watchers: Stebėtojai @@ -1180,10 +1176,6 @@ lt: description_all_columns: AVisi stulpeliai description_issue_category_reassign: Pasirinkti darbo kategoriją description_wiki_subpages_reassign: Pasirinkti naują pagrindinį puslapį - description_date_range_list: Pasirinkitę diapazoną iš sąrašo - description_date_range_interval: Pasirinkite diapazoną pasirinkdami pradžios ir pabaigos datas - description_date_from: Įvesti pradžios datą - description_date_to: Įvesti pabaigos datą text_repository_identifier_info: 'Leidžiamos tik mažosios raidės (a-z), skaitmenys, brūkšneliai ir pabraukimo simboliai.
    Kartą išsaugojus pakeitimai negalimi' label_wiki_page_new: Naujas wiki puslapis label_relations: Ryšiai @@ -1198,5 +1190,31 @@ lt: label_new_object_tab_enabled: Rodyti "+" išskleidžiąmąjį sąrašą error_no_projects_with_tracker_allowed_for_new_issue: Nėra projektų su pėdsekiais, kuriems galima būtų sukurti darbą + field_textarea_font: Šriftas naudojamas teksto sritims + label_font_default: Numatytasis šriftas + label_font_monospace: Lygiaplotis šriftas + label_font_proportional: Įvairiaplotis šriftas + setting_timespan_format: Laiko tarpo formatas + label_table_of_contents: Turinio lentelė + setting_commit_logs_formatting: Pritaikyti teksto formatavimą patvirtinimo žinutėms + setting_mail_handler_enable_regex_delimiters: Įjungti reguliariuosius reiškinius + error_move_of_child_not_possible: 'Darbo dalis %{child} negali būti perkelta į naują + projektą: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Dirbtas laikas negali būti iš naujo paskirtas darbui, kuris bus ištrintas + setting_timelog_required_fields: Privalomi laukai laiko registracijai + label_attribute_of_object: '%{object_name}''s %{name}' + label_user_mail_option_only_assigned: Tik dalykai, kuriuos stebiu arba esu įtrauktas + label_user_mail_option_only_owner: Tik dalykai, kuriuos stebiu arba esu jų savininkas + warning_fields_cleared_on_bulk_edit: Pakeitimai iššauks automatinį reikšmių + pašalinimą iš vieno arba kelių laukų pažymėtiems objektams + field_updated_by: Atnaujino + field_last_updated_by: Paskutinį kartą atnaujino + field_full_width_layout: Viso pločio išdėstymas + label_last_notes: Paskutinės pastabos + field_digest: Kontrolinė suma + field_default_assigned_to: Numatytasis paskirtasis + setting_show_custom_fields_on_registration: Rodyti individualizuotus laukus registracijoje + permission_view_news: Žiūrėti naujienas + label_no_preview_alternative_html: Peržiūra neprieinama. Naudokite failą %{link}. + label_no_preview_download: Atsisiųsti diff --git a/config/locales/lv.yml b/config/locales/lv.yml index 41345cce3..1db2ce002 100644 --- a/config/locales/lv.yml +++ b/config/locales/lv.yml @@ -124,6 +124,8 @@ lv: circular_dependency: "Šī relācija radītu ciklisku atkarību" cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" actionview_instancetag_blank_option: Izvēlieties @@ -546,7 +548,6 @@ lv: label_internal: Iekšējais label_last_changes: "pēdējās %{count} izmaiņas" label_change_view_all: Skatīt visas izmaiņas - label_personalize_page: Pielāgot šo lapu label_comment: Komentārs label_comment_plural: Komentāri label_x_comments: @@ -703,7 +704,6 @@ lv: label_age: Vecums label_change_properties: Mainīt atribūtus label_general: Galvenais - label_more: Vēl label_scm: SCM label_plugins: Spraudņi label_ldap_authentication: LDAP pilnvarošana @@ -713,7 +713,6 @@ lv: label_preferences: Priekšrocības label_chronological_order: Hronoloģiskā kārtībā label_reverse_chronological_order: Apgriezti hronoloģiskā kārtībā - label_planning: Plānošana label_incoming_emails: "Ienākošie e-pasti" label_generate_key: Ģenerēt atslēgu label_issue_watchers: Vērotāji @@ -890,7 +889,6 @@ lv: error_can_not_delete_tracker: This tracker contains issues and cannot be deleted. label_project_copy_notifications: Send email notifications during the project copy field_principal: Principal - label_my_page_block: My page block notice_failed_to_save_members: "Failed to save member(s): %{errors}." text_zoom_out: Zoom out text_zoom_in: Zoom in @@ -901,10 +899,8 @@ lv: project_module_calendar: Calendar button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}" field_text: Text field - label_user_mail_option_only_owner: Tikai par uzdevumiem, ko esmu izveidojis setting_default_notification_option: Default notification option label_user_mail_option_only_my_events: Tikai par uzdevumiem, kurus novēroju vai kuros esmu iesaistījies - label_user_mail_option_only_assigned: Tikai par uzdevumiem, kas ir piešķirti man label_user_mail_option_none: Ne par ko field_member_of_group: Assignee's group field_assigned_to_role: Assignee's role @@ -961,16 +957,12 @@ lv: description_project_scope: Search scope description_filter: Filter description_user_mail_notification: Mail notification settings - description_date_from: Enter start date description_message_content: Message content description_available_columns: Available Columns - description_date_range_interval: Choose range by selecting start and end date description_issue_category_reassign: Choose issue category description_search: Searchfield description_notes: Notes - description_date_range_list: Choose range from list description_choose_project: Projects - description_date_to: Enter end date description_query_sort_criteria_attribute: Sort attribute description_wiki_subpages_reassign: Choose new parent page description_selected_columns: Selected Columns @@ -1202,5 +1194,31 @@ lv: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/mk.yml b/config/locales/mk.yml index 26637b090..d08789ec2 100644 --- a/config/locales/mk.yml +++ b/config/locales/mk.yml @@ -131,6 +131,8 @@ mk: circular_dependency: "Оваа врска ќе креира кружна зависност" cant_link_an_issue_with_a_descendant: "Задача неможе да се поврзе со една од нејзините подзадачи" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" actionview_instancetag_blank_option: Изберете @@ -477,7 +479,6 @@ mk: label_my_page: Мојата страна label_my_account: Мојот профил label_my_projects: Мои проекти - label_my_page_block: Блок елемент label_administration: Администрација label_login: Најави се label_logout: Одјави се @@ -565,7 +566,6 @@ mk: label_internal: Internal label_last_changes: "последни %{count} промени" label_change_view_all: Прегледај ги сите промени - label_personalize_page: Прилагоди ја странава label_comment: Коментар label_comment_plural: Коментари label_x_comments: @@ -723,7 +723,6 @@ mk: label_age: Age label_change_properties: Change properties label_general: Општо - label_more: Повеќе label_scm: SCM label_plugins: Додатоци label_ldap_authentication: LDAP автентикација @@ -733,7 +732,6 @@ mk: label_preferences: Preferences label_chronological_order: Во хронолошки ред label_reverse_chronological_order: In reverse chronological order - label_planning: Планирање label_incoming_emails: Дојдовни е-пораки label_generate_key: Генерирај клуч label_issue_watchers: Watchers @@ -906,10 +904,8 @@ mk: button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}" field_text: Text field - label_user_mail_option_only_owner: Only for things I am the owner of setting_default_notification_option: Default notification option label_user_mail_option_only_my_events: Only for things I watch or I'm involved in - label_user_mail_option_only_assigned: Only for things I am assigned to label_user_mail_option_none: No events field_member_of_group: Assignee's group field_assigned_to_role: Assignee's role @@ -966,16 +962,12 @@ mk: description_project_scope: Search scope description_filter: Filter description_user_mail_notification: Mail notification settings - description_date_from: Enter start date description_message_content: Message content description_available_columns: Available Columns - description_date_range_interval: Choose range by selecting start and end date description_issue_category_reassign: Choose issue category description_search: Searchfield description_notes: Notes - description_date_range_list: Choose range from list description_choose_project: Projects - description_date_to: Enter end date description_query_sort_criteria_attribute: Sort attribute description_wiki_subpages_reassign: Choose new parent page description_selected_columns: Selected Columns @@ -1208,5 +1200,31 @@ mk: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/mn.yml b/config/locales/mn.yml index 943eb5a3d..d608b0855 100644 --- a/config/locales/mn.yml +++ b/config/locales/mn.yml @@ -130,6 +130,8 @@ mn: circular_dependency: "Энэ харьцаа нь гинжин(рекурсив) харьцаа үүсгэх юм байна" cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" actionview_instancetag_blank_option: Сонгоно уу @@ -552,7 +554,6 @@ mn: label_internal: Дотоод label_last_changes: "сүүлийн %{count} өөрчлөлтүүд" label_change_view_all: Бүх өөрчлөлтүүдийг харах - label_personalize_page: Энэ хуудсыг өөрт зориулан өөрчлөх label_comment: Тайлбар label_comment_plural: Тайлбарууд label_x_comments: @@ -709,7 +710,6 @@ mn: label_age: Нас label_change_properties: Тохиргоог өөрчлөх label_general: Ерөнхий - label_more: Цааш нь label_scm: SCM label_plugins: Модулууд label_ldap_authentication: LDAP нэвтрэх горим @@ -719,7 +719,6 @@ mn: label_preferences: Тохиргоо label_chronological_order: Цагаан толгойн үсгийн дарааллаар label_reverse_chronological_order: Урвуу цагаан толгойн үсгийн дарааллаар - label_planning: Төлөвлөлт label_incoming_emails: Ирсэн мэйлүүд label_generate_key: Түлхүүр үүсгэх label_issue_watchers: Ажиглагчид @@ -897,7 +896,6 @@ mn: error_can_not_remove_role: This role is in use and can not be deleted. error_can_not_delete_tracker: This tracker contains issues and cannot be deleted. field_principal: Principal - label_my_page_block: My page block notice_failed_to_save_members: "Failed to save member(s): %{errors}." text_zoom_out: Zoom out text_zoom_in: Zoom in @@ -908,10 +906,8 @@ mn: project_module_calendar: Calendar button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}" field_text: Text field - label_user_mail_option_only_owner: Only for things I am the owner of setting_default_notification_option: Default notification option label_user_mail_option_only_my_events: Only for things I watch or I'm involved in - label_user_mail_option_only_assigned: Only for things I am assigned to label_user_mail_option_none: No events field_member_of_group: Assignee's group field_assigned_to_role: Assignee's role @@ -968,16 +964,12 @@ mn: description_project_scope: Search scope description_filter: Filter description_user_mail_notification: Mail notification settings - description_date_from: Enter start date description_message_content: Message content description_available_columns: Available Columns - description_date_range_interval: Choose range by selecting start and end date description_issue_category_reassign: Choose issue category description_search: Searchfield description_notes: Notes - description_date_range_list: Choose range from list description_choose_project: Projects - description_date_to: Enter end date description_query_sort_criteria_attribute: Sort attribute description_wiki_subpages_reassign: Choose new parent page description_selected_columns: Selected Columns @@ -1209,5 +1201,31 @@ mn: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 2c99a6170..293454971 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -128,52 +128,54 @@ nl: circular_dependency: "Deze relatie zou een circulaire afhankelijkheid tot gevolg hebben" cant_link_an_issue_with_a_descendant: "Een issue kan niet gelinked worden met een subtask" earlier_than_minimum_start_date: "kan niet eerder zijn dan %{date} wegens voorafgaande issues" + 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" - actionview_instancetag_blank_option: Selecteer + actionview_instancetag_blank_option: Selecteren - button_activate: Activeer - button_add: Voeg toe - button_annotate: Annoteer - button_apply: Pas toe - button_archive: Archiveer + button_activate: Activeren + button_add: Toevoegen + button_annotate: Annoteren + button_apply: Toepassen + button_archive: Archiveren button_back: Terug - button_cancel: Annuleer - button_change: Wijzig - button_change_password: Wijzig wachtwoord - button_check_all: Selecteer alle - button_clear: Leeg maken - button_configure: Configureer - button_copy: Kopiëer - button_create: Maak - button_delete: Verwijder + button_cancel: Annuleren + button_change: Wijzigen + button_change_password: Wachtwoord wijzigen + button_check_all: Alles selecteren + button_clear: Leegmaken + button_configure: Configureren + button_copy: Kopiëren + button_create: Aanmaken + button_delete: Verwijderen button_download: Download - button_edit: Bewerk + button_edit: Bewerken button_list: Lijst - button_lock: Sluit - button_log_time: Registreer tijd + button_lock: Vergrendelen + button_log_time: Tijd registreren button_login: Inloggen button_move: Verplaatsen - button_quote: Citaat + button_quote: Citeren button_rename: Hernoemen - button_reply: Antwoord - button_reset: Reset - button_rollback: Rollback naar deze versie - button_save: Bewaren - button_sort: Sorteer + button_reply: Antwoorden + button_reset: Herstellen + button_rollback: Terugdraaien naar deze versie + button_save: Opslaan + button_sort: Sorteren button_submit: Toevoegen - button_test: Test - button_unarchive: Dearchiveer - button_uncheck_all: Deselecteer alle - button_unlock: Open - button_unwatch: Niet meer monitoren - button_update: Update - button_view: Bekijken - button_watch: Monitor + button_test: Testen + button_unarchive: Dearchiveren + button_uncheck_all: Deselecteren + button_unlock: Ontgrendelen + button_unwatch: Niet meer volgen + button_update: Bijwerken + button_view: Weergeven + button_watch: Volgen default_activity_design: Ontwerp default_activity_development: Ontwikkeling default_doc_category_tech: Technische documentatie default_doc_category_user: Gebruikersdocumentatie - default_issue_status_in_progress: In Progress + default_issue_status_in_progress: In uitvoering default_issue_status_closed: Gesloten default_issue_status_feedback: Terugkoppeling default_issue_status_new: Nieuw @@ -183,30 +185,30 @@ nl: default_priority_immediate: Onmiddellijk default_priority_low: Laag default_priority_normal: Normaal - default_priority_urgent: Spoed + default_priority_urgent: Dringend default_role_developer: Ontwikkelaar default_role_manager: Manager default_role_reporter: Rapporteur default_tracker_bug: Bug default_tracker_feature: Feature default_tracker_support: Support - enumeration_activities: Activiteiten (tijdtracking) + enumeration_activities: Activiteiten (tijdregistratie) enumeration_doc_categories: Documentcategorieën enumeration_issue_priorities: Issueprioriteiten - error_can_t_load_default_data: "De standaard configuratie kon niet worden geladen: %{value}" - error_issue_not_found_in_project: 'Deze issue is niet gevonden of behoort niet toe tot dit project.' + error_can_t_load_default_data: "De standaardconfiguratie kan niet worden geladen: %{value}" + error_issue_not_found_in_project: 'Deze issue kan niet gevonden worden of behoort niet toe aan dit project.' error_scm_annotate: "Er kan geen commentaar toegevoegd worden." - error_scm_command_failed: "Er trad een fout op tijdens de poging om verbinding te maken met de repository: %{value}" - error_scm_not_found: "Deze ingang of revisie bestaat niet in de repository." + error_scm_command_failed: "Er is een fout opgetreden tijdens het verbinding maken met de repository: %{value}" + error_scm_not_found: "Dit item of deze revisie bestaat niet in de repository." field_account: Account field_activity: Activiteit field_admin: Beheerder - field_assignable: Issues kunnen toegewezen worden aan deze rol + field_assignable: Issues kunnen aan deze rol toegewezen worden field_assigned_to: Toegewezen aan - field_attr_firstname: Voornaam attribuut - field_attr_lastname: Achternaam attribuut - field_attr_login: Login attribuut - field_attr_mail: E-mail attribuut + field_attr_firstname: Voornaamattribuut + field_attr_lastname: Achternaamattribuut + field_attr_login: Loginattribuut + field_attr_mail: E-mailattribuut field_auth_source: Authenticatiemethode field_author: Auteur field_base_dn: Base DN @@ -214,13 +216,13 @@ nl: field_column_names: Kolommen field_comments: Commentaar field_comments_sorting: Commentaar weergeven - field_created_on: Aangemaakt + field_created_on: Aangemaakt op field_default_value: Standaardwaarde field_delay: Vertraging field_description: Beschrijving - field_done_ratio: "% Gereed" + field_done_ratio: "% voltooid" field_downloads: Downloads - field_due_date: Verwachte datum gereed + field_due_date: Verwachte einddatum field_effective_date: Datum field_estimated_hours: Geschatte tijd field_field_format: Formaat @@ -229,16 +231,16 @@ nl: field_firstname: Voornaam field_fixed_version: Versie field_hide_mail: Verberg mijn e-mailadres - field_homepage: Homepage + field_homepage: Homepagina field_host: Host field_hours: Uren field_identifier: Identificatiecode field_is_closed: Issue gesloten field_is_default: Standaard - field_is_filter: Gebruikt als een filter + field_is_filter: Als filter gebruiken field_is_for_all: Voor alle projecten field_is_in_roadmap: Issues weergegeven in roadmap - field_is_public: Publiek + field_is_public: Openbaar field_is_required: Verplicht field_issue: Issue field_issue_to: Gerelateerd issue @@ -247,7 +249,7 @@ nl: field_lastname: Achternaam field_login: Gebruikersnaam field_mail: E-mail - field_mail_notification: Mail notificaties + field_mail_notification: E-mailnotificaties field_max_length: Maximale lengte field_min_length: Minimale lengte field_name: Naam @@ -258,11 +260,11 @@ nl: field_parent_title: Bovenliggende pagina field_password: Wachtwoord field_password_confirmation: Bevestig wachtwoord - field_port: Port + field_port: Poort field_possible_values: Mogelijke waarden field_priority: Prioriteit field_project: Project - field_redirect_existing_links: Verwijs bestaande links door + field_redirect_existing_links: Bestaande links doorverwijzen field_regexp: Reguliere expressie field_role: Rol field_searchable: Doorzoekbaar @@ -277,7 +279,7 @@ nl: field_title: Titel field_tracker: Tracker field_type: Type - field_updated_on: Gewijzigd + field_updated_on: Laatst gewijzigd op field_url: URL field_user: Gebruiker field_value: Waarde @@ -295,7 +297,7 @@ nl: general_text_yes: 'ja' label_activity: Activiteit label_add_another_file: Ander bestand toevoegen - label_add_note: Voeg een notitie toe + label_add_note: Notitie toevoegen label_added: toegevoegd label_added_time_by: "Toegevoegd door %{author} %{age} geleden" label_administration: Administratie @@ -304,12 +306,12 @@ nl: label_all: alle label_all_time: alles label_all_words: Alle woorden - label_and_its_subprojects: "%{value} en zijn subprojecten." + label_and_its_subprojects: "%{value} en de subprojecten." label_applied_status: Toegekende status label_assigned_to_me_issues: Aan mij toegewezen issues label_associated_revisions: Geassociëerde revisies label_attachment: Bestand - label_attachment_delete: Verwijder bestand + label_attachment_delete: Bestand verwijderen label_attachment_new: Nieuw bestand label_attachment_plural: Bestanden label_attribute: Attribuut @@ -323,14 +325,14 @@ nl: label_board: Forum label_board_new: Nieuw forum label_board_plural: Forums - label_boolean: Boolean - label_browse: Blader - label_bulk_edit_selected_issues: Bewerk geselecteerde issues in bulk + label_boolean: Booleaanse waarde + label_browse: Bladeren + label_bulk_edit_selected_issues: Geselecteerde issues in bulk bewerken label_calendar: Kalender label_change_plural: Wijzigingen label_change_properties: Eigenschappen wijzigen - label_change_status: Wijzig status - label_change_view_all: Bekijk alle wijzigingen + label_change_status: Status wijzigen + label_change_view_all: Alle wijzigingen weergeven label_changes_details: Details van alle wijzigingen label_changeset_plural: Changesets label_chronological_order: In chronologische volgorde @@ -345,10 +347,10 @@ nl: one: 1 gesloten other: "%{count} gesloten" label_comment: Commentaar - label_comment_add: Voeg commentaar toe + label_comment_add: Commentaar toevoegen label_comment_added: Commentaar toegevoegd - label_comment_delete: Verwijder commentaar - label_comment_plural: Commentaar + label_comment_delete: Commentaar verwijderen + label_comment_plural: Commentaren label_x_comments: zero: geen commentaar one: 1x commentaar @@ -361,16 +363,16 @@ nl: label_copy_workflow_from: Kopieer workflow van label_current_status: Huidige status label_current_version: Huidige versie - label_custom_field: Specifiek veld - label_custom_field_new: Nieuw specifiek veld - label_custom_field_plural: Specifieke velden + label_custom_field: Vrij veld + label_custom_field_new: Nieuw vrij veld + label_custom_field_plural: Vrije velden label_date: Datum label_date_from: Van label_date_range: Datumbereik label_date_to: Tot label_day_plural: dagen label_default: Standaard - label_default_columns: Standaard kolommen. + label_default_columns: Standaardkolommen. label_deleted: verwijderd label_details: Details label_diff_inline: inline @@ -393,16 +395,16 @@ nl: label_f_hour: "%{value} uur" label_f_hour_plural: "%{value} uren" label_feed_plural: Feeds - label_feeds_access_key_created_on: "Atom toegangssleutel %{value} geleden gemaakt." + label_feeds_access_key_created_on: "Atom-toegangssleutel %{value} geleden gemaakt" label_file_added: Bestand toegevoegd label_file_plural: Bestanden - label_filter_add: Voeg filter toe + label_filter_add: Filter toevoegen label_filter_plural: Filters - label_float: Float + label_float: Decimaal getal label_follows: volgt op label_gantt: Gantt label_general: Algemeen - label_generate_key: Genereer een sleutel + label_generate_key: Een sleutel genereren label_help: Help label_history: Geschiedenis label_home: Home @@ -414,22 +416,22 @@ nl: label_index_by_title: Indexeer op titel label_information: Informatie label_information_plural: Informatie - label_integer: Integer + label_integer: Getal label_internal: Intern label_issue: Issue label_issue_added: Issue toegevoegd - label_issue_category: Issue categorie + label_issue_category: Issuecategorie label_issue_category_new: Nieuwe categorie label_issue_category_plural: Issuecategorieën label_issue_new: Nieuw issue label_issue_plural: Issues - label_issue_status: Issue status + label_issue_status: Issuestatus label_issue_status_new: Nieuwe status - label_issue_status_plural: Issue statussen - label_issue_tracking: Issue-tracking + label_issue_status_plural: Issuestatussen + label_issue_tracking: Issue tracking label_issue_updated: Issue bijgewerkt - label_issue_view_all: Bekijk alle issues - label_issue_watchers: Monitoren + label_issue_view_all: Alle issues bekijken + label_issue_watchers: Volgers label_issues_by: "Issues door %{value}" label_jump_to_a_project: Ga naar een project... label_language_based: Taal gebaseerd @@ -443,7 +445,7 @@ nl: label_ldap_authentication: LDAP authenticatie label_less_than_ago: minder dan x dagen geleden label_list: Lijst - label_loading: Laden... + label_loading: Bezig met laden... label_logged_as: Ingelogd als label_login: Inloggen label_logout: Uitloggen @@ -461,22 +463,21 @@ nl: label_module_plural: Modules label_month: Maand label_months_from: maanden vanaf - label_more: Meer label_more_than_ago: meer dan x dagen geleden label_my_account: Mijn account label_my_page: Mijn pagina label_my_projects: Mijn projecten label_new: Nieuw - label_new_statuses_allowed: Nieuwe toegestane statussen + label_new_statuses_allowed: Nieuw toegestane statussen label_news: Nieuws label_news_added: Nieuws toegevoegd label_news_latest: Laatste nieuws - label_news_new: Voeg nieuws toe + label_news_new: Nieuws toevoegen label_news_plural: Nieuws - label_news_view_all: Bekijk al het nieuws + label_news_view_all: Alle nieuws weergeven label_next: Volgende label_no_change_option: (Geen wijziging) - label_no_data: Geen gegevens om te tonen + label_no_data: Er zijn geen gegevens om weer te geven label_nobody: niemand label_none: geen label_not_contains: bevat niet @@ -487,12 +488,10 @@ nl: label_options: Opties label_overall_activity: Activiteit label_overview: Overzicht - label_password_lost: Wachtwoord verloren + label_password_lost: Wachtwoord vergeten label_permissions: Permissies label_permissions_report: Permissierapport - label_personalize_page: Personaliseer deze pagina - label_planning: Planning - label_please_login: Log a.u.b. in + label_please_login: Gelieve in te loggen label_plugins: Plugins label_precedes: gaat vooraf aan label_preferences: Voorkeuren @@ -511,15 +510,15 @@ nl: label_query: Eigen zoekopdracht label_query_new: Nieuwe zoekopdracht label_query_plural: Eigen zoekopdrachten - label_read: Lees... - label_register: Registreer + label_read: Lees meer... + label_register: Registreren label_registered_on: Geregistreerd op - label_registration_activation_by_email: accountactivatie per e-mail - label_registration_automatic_activation: automatische accountactivatie - label_registration_manual_activation: handmatige accountactivatie + label_registration_activation_by_email: accountactivering per e-mail + label_registration_automatic_activation: automatische accountactivering + label_registration_manual_activation: handmatige accountactivering label_related_issues: Gerelateerde issues label_relates_to: gerelateerd aan - label_relation_delete: Verwijder relatie + label_relation_delete: Relatie verwijderen label_relation_new: Nieuwe relatie label_renamed: hernoemd label_reply_plural: Antwoorden @@ -544,17 +543,17 @@ nl: label_search: Zoeken label_search_titles_only: Enkel titels doorzoeken label_send_information: Stuur accountinformatie naar de gebruiker - label_send_test_email: Stuur een test e-mail + label_send_test_email: Stuur een e-mail om te testen label_settings: Instellingen - label_show_completed_versions: Toon afgeronde versies + label_show_completed_versions: Afgeronde versies weergeven label_sort_by: "Sorteer op %{value}" label_sort_higher: Verplaats naar boven label_sort_highest: Verplaats naar begin label_sort_lower: Verplaats naar beneden - label_sort_lowest: Verplaats naar eind + label_sort_lowest: Verplaats naar einde label_spent_time: Gespendeerde tijd label_statistics: Statistieken - label_stay_logged_in: Blijf ingelogd + label_stay_logged_in: Ingelogd blijven label_string: Tekst label_subproject_plural: Subprojecten label_text: Lange tekst @@ -576,81 +575,81 @@ nl: label_user_activity: "%{value}'s activiteit" label_user_mail_no_self_notified: Ik wil niet op de hoogte gehouden worden van mijn eigen wijzigingen label_user_mail_option_all: "Bij elke gebeurtenis in al mijn projecten..." - label_user_mail_option_selected: "Enkel bij elke gebeurtenis op het geselecteerde project..." + label_user_mail_option_selected: "Enkel bij iedere gebeurtenis op het geselecteerde project..." label_user_new: Nieuwe gebruiker label_user_plural: Gebruikers label_version: Versie label_version_new: Nieuwe versie label_version_plural: Versies - label_view_diff: Bekijk verschillen - label_view_revisions: Bekijk revisies - label_watched_issues: Gemonitorde issues + label_view_diff: Verschillen weergeven + label_view_revisions: Revisies weergeven + label_watched_issues: Gevolgde issues label_week: Week label_wiki: Wiki - label_wiki_edit: Wiki edit - label_wiki_edit_plural: Wiki edits + label_wiki_edit: Wiki-aanpassing + label_wiki_edit_plural: Wiki-aanpassingen label_wiki_page: Wikipagina label_wiki_page_plural: Wikipagina's label_workflow: Workflow label_year: Jaar label_yesterday: gisteren - mail_body_account_activation_request: "Een nieuwe gebruiker (%{value}) is geregistreerd. Zijn account wacht op uw akkoord:" + mail_body_account_activation_request: "Een nieuwe gebruiker (%{value}) heeft zich geregistreerd. Zijn account wacht op uw akkoord:" mail_body_account_information: Uw account gegevens mail_body_account_information_external: "U kunt uw account (%{value}) gebruiken om in te loggen." - mail_body_lost_password: 'Gebruik de volgende link om uw wachtwoord te wijzigen:' - mail_body_register: 'Gebruik de volgende link om uw account te activeren:' + mail_body_lost_password: 'Gebruik volgende link om uw wachtwoord te wijzigen:' + mail_body_register: 'Gebruik volgende link om uw account te activeren:' mail_body_reminder: "%{count} issue(s) die aan u toegewezen zijn en voldaan moeten zijn in de komende %{days} dagen:" - mail_subject_account_activation_request: "%{value} accountactivatieverzoek" + mail_subject_account_activation_request: "%{value} account activeringsverzoek" mail_subject_lost_password: "uw %{value} wachtwoord" - mail_subject_register: "uw %{value} accountactivatie" + mail_subject_register: "uw %{value} accountactivering" mail_subject_reminder: "%{count} issue(s) die voldaan moeten zijn in de komende %{days} dagen." - notice_account_activated: uw account is geactiveerd. u kunt nu inloggen. + notice_account_activated: Uw account is geactiveerd. U kunt nu inloggen. notice_account_invalid_credentials: Incorrecte gebruikersnaam of wachtwoord - notice_account_lost_email_sent: Er is een e-mail naar u verstuurd met instructies over het kiezen van een nieuw wachtwoord. + notice_account_lost_email_sent: Er is een e-mail naar u verzonden met instructies over de keuze van een nieuw wachtwoord. notice_account_password_updated: Wachtwoord is met succes gewijzigd - notice_account_pending: "Uw account is aangemaakt, maar wacht nog op goedkeuring van de beheerder." + notice_account_pending: Uw account is aangemaakt, maar wacht nog op goedkeuring van een beheerder. notice_account_unknown_email: Onbekende gebruiker. - notice_account_updated: Account is met succes gewijzigd - notice_account_wrong_password: Incorrect wachtwoord - notice_can_t_change_password: Dit account gebruikt een externe bron voor authenticatie. Het is niet mogelijk om het wachtwoord te veranderen. - notice_default_data_loaded: Standaard configuratie succesvol geladen. - notice_email_error: "Er is een fout opgetreden tijdens het versturen van (%{value})" + notice_account_updated: Account is succesvol gewijzigd + notice_account_wrong_password: Ongeldig wachtwoord + notice_can_t_change_password: Deze account gebruikt een externe authenticatiebron. Het is niet mogelijk om het wachtwoord te veranderen. + notice_default_data_loaded: Standaardconfiguratie succesvol geladen. + notice_email_error: "Er is een fout opgetreden bij het versturen van (%{value})" notice_email_sent: "Een e-mail werd verstuurd naar %{value}" notice_failed_to_save_issues: "Fout bij bewaren van %{count} issue(s) (%{total} geselecteerd): %{ids}." - notice_feeds_access_key_reseted: Je Atom toegangssleutel werd gereset. - notice_file_not_found: De pagina die u probeerde te benaderen bestaat niet of is verwijderd. - notice_locking_conflict: De gegevens zijn gewijzigd door een andere gebruiker. - notice_no_issue_selected: "Er is geen issue geselecteerd. Selecteer de issue die u wilt bewerken." - notice_not_authorized: Het is u niet toegestaan deze pagina te raadplegen. + notice_feeds_access_key_reseted: Uw Atom-toegangssleutel werd opnieuw ingesteld. + notice_file_not_found: De pagina, die u probeerde te benaderen, bestaat niet of is verwijderd. + notice_locking_conflict: De gegevens werden reeds eerder gewijzigd door een andere gebruiker. + notice_no_issue_selected: "Er is geen issue geselecteerd. Selecteer het issue dat u wil bewerken." + notice_not_authorized: U heeft niet de juiste machtigingen om deze pagina te raadplegen. notice_successful_connection: Verbinding succesvol. notice_successful_create: Succesvol aangemaakt. notice_successful_delete: Succesvol verwijderd. - notice_successful_update: Wijzigen succesvol. - notice_unable_delete_version: Niet mogelijk om deze versie te verwijderen. - permission_add_issue_notes: Voeg notities toe - permission_add_issue_watchers: Voeg monitors toe - permission_add_issues: Voeg issues toe - permission_add_messages: Voeg berichten toe + notice_successful_update: Succesvol gewijzigd. + notice_unable_delete_version: Het is niet mogelijk om deze versie te verwijderen. + permission_add_issue_notes: Notities toevoegen + permission_add_issue_watchers: Volgers toevoegen + permission_add_issues: Issues toevoegen + permission_add_messages: Berichten toevoegen permission_browse_repository: Repository doorbladeren - permission_comment_news: Nieuws commentaar geven - permission_commit_access: Commit toegang + permission_comment_news: Commentaar toevoegen bij nieuws + permission_commit_access: Commit-rechten permission_delete_issues: Issues verwijderen permission_delete_messages: Berichten verwijderen permission_delete_own_messages: Eigen berichten verwijderen - permission_delete_wiki_pages: Wiki pagina's verwijderen + permission_delete_wiki_pages: Wikipagina's verwijderen permission_delete_wiki_pages_attachments: Bijlagen verwijderen permission_edit_issue_notes: Notities bewerken permission_edit_issues: Issues bewerken permission_edit_messages: Berichten bewerken permission_edit_own_issue_notes: Eigen notities bewerken permission_edit_own_messages: Eigen berichten bewerken - permission_edit_own_time_entries: Eigen tijdlogboek bewerken + permission_edit_own_time_entries: Eigen tijdregistraties bewerken permission_edit_project: Project bewerken - permission_edit_time_entries: Tijdlogboek bewerken - permission_edit_wiki_pages: Wiki pagina's bewerken - permission_log_time: Gespendeerde tijd loggen + permission_edit_time_entries: Tijdregistraties bewerken + permission_edit_wiki_pages: Wikipagina's bewerken + permission_log_time: Tijdregistraties boeken permission_manage_boards: Forums beheren - permission_manage_categories: Issue-categorieën beheren + permission_manage_categories: Issuecategorieën beheren permission_manage_files: Bestanden beheren permission_manage_issue_relations: Issuerelaties beheren permission_manage_members: Leden beheren @@ -668,10 +667,10 @@ nl: permission_view_changesets: Changesets bekijken permission_view_documents: Documenten bekijken permission_view_files: Bestanden bekijken - permission_view_gantt: Gantt grafiek bekijken - permission_view_issue_watchers: Monitorlijst bekijken + permission_view_gantt: Gantt-grafiek bekijken + permission_view_issue_watchers: Lijst met volgers bekijken permission_view_messages: Berichten bekijken - permission_view_time_entries: Gespendeerde tijd bekijken + permission_view_time_entries: Tijdregistraties bekijken permission_view_wiki_edits: Wikihistorie bekijken permission_view_wiki_pages: Wikipagina's bekijken project_module_boards: Forums @@ -680,103 +679,103 @@ nl: project_module_issue_tracking: Issue tracking project_module_news: Nieuws project_module_repository: Repository - project_module_time_tracking: Tijd tracking + project_module_time_tracking: Tijdregistratie project_module_wiki: Wiki - setting_activity_days_default: Aantal dagen getoond bij het tabblad "Activiteit" + setting_activity_days_default: Aantal weergegeven dagen bij het tabblad "Activiteit" setting_app_subtitle: Applicatieondertitel setting_app_title: Applicatietitel - setting_attachment_max_size: Attachment max. grootte - setting_autofetch_changesets: Haal commits automatisch op + setting_attachment_max_size: Max. grootte bijlage + setting_autofetch_changesets: Commits automatisch ophalen setting_autologin: Automatisch inloggen setting_bcc_recipients: Blind carbon copy ontvangers (bcc) - setting_commit_fix_keywords: Gefixeerde trefwoorden + setting_commit_fix_keywords: Vaste trefwoorden setting_commit_ref_keywords: Refererende trefwoorden - setting_cross_project_issue_relations: Sta cross-project issuerelaties toe + setting_cross_project_issue_relations: Issuerelaties tussen projecten toelaten setting_date_format: Datumformaat - setting_default_language: Standaard taal - setting_default_projects_public: Nieuwe projecten zijn standaard publiek - setting_diff_max_lines_displayed: Max aantal diff regels weer te geven - setting_display_subprojects_issues: Standaard issues van subproject tonen - setting_emails_footer: E-mails voettekst + setting_default_language: Standaardtaal + setting_default_projects_public: Nieuwe projecten zijn standaard openbaar + setting_diff_max_lines_displayed: Max aantal weergegeven diff regels + setting_display_subprojects_issues: Standaardissues van subproject weergeven + setting_emails_footer: Voettekst voor e-mails setting_enabled_scm: SCM ingeschakeld setting_feeds_limit: Feedinhoudlimiet setting_gravatar_enabled: Gebruik Gravatar gebruikersiconen setting_host_name: Hostnaam - setting_issue_list_default_columns: Standaardkolommen getoond op de lijst met issues + setting_issue_list_default_columns: Zichtbare standaardkolommen in lijst met issues setting_issues_export_limit: Max aantal te exporteren issues setting_login_required: Authenticatie vereist - setting_mail_from: Afzender e-mail adres - setting_mail_handler_api_enabled: Schakel WS in voor inkomende mail. - setting_mail_handler_api_key: API sleutel + setting_mail_from: E-mailadres afzender + setting_mail_handler_api_enabled: Schakel WS in voor inkomende e-mail. + setting_mail_handler_api_key: API-sleutel setting_per_page_options: Aantal objecten per pagina (opties) setting_plain_text_mail: platte tekst (geen HTML) setting_protocol: Protocol setting_self_registration: Zelfregistratie toegestaan - setting_sequential_project_identifiers: Genereer sequentiële projectidentiteiten + setting_sequential_project_identifiers: Sequentiële projectidentiteiten genereren setting_sys_api_enabled: Gebruik WS voor repository beheer setting_text_formatting: Tekstformaat - setting_time_format: Tijd formaat - setting_user_format: Gebruikers weergaveformaat + setting_time_format: Tijdformaat + setting_user_format: Weergaveformaat gebruikers setting_welcome_text: Welkomsttekst setting_wiki_compression: Wikigeschiedenis comprimeren status_active: actief status_locked: vergrendeld status_registered: geregistreerd text_are_you_sure: Weet u het zeker? - text_assign_time_entries_to_project: Gerapporteerde uren toevoegen aan dit project + text_assign_time_entries_to_project: Gerapporteerde uren aan dit project toevoegen text_caracters_maximum: "%{count} van maximum aantal tekens." text_caracters_minimum: "Moet minstens %{count} karakters lang zijn." text_comma_separated: Meerdere waarden toegestaan (kommagescheiden). text_default_administrator_account_changed: Standaard beheerderaccount gewijzigd - text_destroy_time_entries: Verwijder gerapporteerde uren - text_destroy_time_entries_question: "%{hours} uren werden gerapporteerd op de issue(s) die u wilde verwijderen. Wat wil u doen?" - text_diff_truncated: '... Deze diff werd afgekort omdat het de maximale weer te geven karakters overschreed.' - text_email_delivery_not_configured: "E-mailbezorging is niet geconfigureerd. Mededelingen zijn uitgeschakeld.\nConfigureer uw SMTP server in config/configuration.yml en herstart de applicatie om dit te activeren." - text_enumeration_category_reassign_to: 'Wijs de volgende waarde toe:' + text_destroy_time_entries: Gerapporteerde uren verwijderen + text_destroy_time_entries_question: "%{hours} uren werden gerapporteerd op de issue(s) die u wilt verwijderen. Wat wilt u doen?" + text_diff_truncated: '... Deze diff werd ingekort omdat het de maximale weer te geven karakters overschrijdt.' + text_email_delivery_not_configured: "E-mailbezorging is niet geconfigureerd. Mededelingen zijn uitgeschakeld.\nConfigureer uw SMTP server in config/configuration.yml en herstart de applicatie om e-mailbezorging te activeren." + text_enumeration_category_reassign_to: 'Volgende waarde toewijzen:' text_enumeration_destroy_question: "%{count} objecten zijn toegewezen aan deze waarde." - text_file_repository_writable: Bestandsrepository beschrijfbaar + text_file_repository_writable: Bestandsrepository schrijfbaar text_issue_added: "Issue %{id} is gerapporteerd (door %{author})." - text_issue_category_destroy_assignments: Verwijder toewijzingen aan deze categorie - text_issue_category_destroy_question: "Er zijn issues (%{count}) aan deze categorie toegewezen. Wat wilt u hiermee doen ?" - text_issue_category_reassign_to: Issues opnieuw toewijzen aan deze categorie + text_issue_category_destroy_assignments: Toewijzingen aan deze categorie verwijderen + text_issue_category_destroy_question: "Er zijn issues (%{count}) aan deze categorie toegewezen. Wat wilt u doen?" + text_issue_category_reassign_to: Issues opnieuw aan deze categorie toewijzen text_issue_updated: "Issue %{id} is gewijzigd (door %{author})." - text_issues_destroy_confirmation: 'Weet u zeker dat u deze issue(s) wil verwijderen?' + text_issues_destroy_confirmation: 'Weet u zeker dat u deze issue(s) wilt verwijderen?' text_issues_ref_in_commit_messages: Opzoeken en aanpassen van issues in commitberichten text_length_between: "Lengte tussen %{min} en %{max} tekens." - text_load_default_configuration: Laad de standaardconfiguratie - text_min_max_length_info: 0 betekent geen restrictie - text_no_configuration_data: "Rollen, trackers, issue statussen en workflows zijn nog niet geconfigureerd.\nHet is ten zeerste aangeraden om de standaard configuratie in te laden. U kunt deze aanpassen nadat deze is ingeladen." - text_plugin_assets_writable: Plugin assets directory beschrijfbaar + text_load_default_configuration: Standaardconfiguratie laden + text_min_max_length_info: 0 betekent geen beperking + text_no_configuration_data: "Rollen, trackers, issuestatussen en workflows zijn nog niet geconfigureerd.\nHet is ten zeerste aangeraden om de standaardconfiguratie in te laden. U kunt deze aanpassen nadat deze is ingeladen." + text_plugin_assets_writable: Plugin assets map schrijfbaar text_project_destroy_confirmation: Weet u zeker dat u dit project en alle gerelateerde gegevens wilt verwijderen? - text_project_identifier_info: 'Alleen kleine letter (a-z), cijfers, streepjes en liggende streepjes zijn toegestaan.
    Eenmaal opgeslagen kan de identifier niet worden gewijzigd.' + text_project_identifier_info: 'Alleen kleine letters (a-z), cijfers, streepjes en liggende streepjes zijn toegestaan.
    Eenmaal opgeslagen kan de identifier niet worden gewijzigd.' text_reassign_time_entries: 'Gerapporteerde uren opnieuw toewijzen:' text_regexp_info: bv. ^[A-Z0-9]+$ - text_repository_usernames_mapping: "Koppel de Redminegebruikers aan gebruikers in de repository log.\nGebruikers met dezelfde Redmine en repository gebruikersnaam of email worden automatisch gekoppeld." + text_repository_usernames_mapping: "Koppel de Redmine-gebruikers aan gebruikers in de repository log.\nGebruikers met dezelfde Redmine en repository gebruikersnaam of e-mail worden automatisch gekoppeld." text_rmagick_available: RMagick beschikbaar (optioneel) - text_select_mail_notifications: Selecteer acties waarvoor mededelingen via mail moeten worden verstuurd. + text_select_mail_notifications: Selecteer acties waarvoor mededelingen via e-mail moeten worden verstuurd. text_select_project_modules: 'Selecteer de modules die u wilt gebruiken voor dit project:' text_status_changed_by_changeset: "Toegepast in changeset %{value}." text_subprojects_destroy_warning: "De subprojecten: %{value} zullen ook verwijderd worden." - text_tip_issue_begin_day: issue die op deze dag begint - text_tip_issue_begin_end_day: issue die op deze dag begint en eindigt - text_tip_issue_end_day: issue die op deze dag eindigt + text_tip_issue_begin_day: issue begint op deze dag + text_tip_issue_begin_end_day: issue begint en eindigt op deze dag + text_tip_issue_end_day: issue eindigt op deze dag text_tracker_no_workflow: Geen workflow gedefinieerd voor deze tracker - text_unallowed_characters: Niet toegestane tekens - text_user_mail_option: "Bij niet-geselecteerde projecten zult u enkel mededelingen ontvangen voor issues die u monitort of waar u bij betrokken bent (als auteur of toegewezen persoon)." + text_unallowed_characters: Ongeldige tekens + text_user_mail_option: "Bij niet-geselecteerde projecten zal u enkel mededelingen ontvangen voor issues die u volgt of waar u bij betrokken bent (als auteur of toegewezen persoon)." text_user_wrote: "%{value} schreef:" - text_wiki_destroy_confirmation: Weet u zeker dat u deze wiki en zijn inhoud wenst te verwijderen? + text_wiki_destroy_confirmation: Weet u zeker dat u deze wiki en de inhoud wenst te verwijderen? text_workflow_edit: Selecteer een rol en een tracker om de workflow te wijzigen warning_attachments_not_saved: "%{count} bestand(en) konden niet opgeslagen worden." - button_create_and_continue: Maak en ga verder + button_create_and_continue: Aanmaken en verdergaan text_custom_field_possible_values_info: 'Per lijn een waarde' - label_display: Toon + label_display: Weergave field_editable: Bewerkbaar setting_repository_log_display_limit: Max aantal revisies zichbaar - setting_file_max_size_displayed: Max grootte van tekst bestanden inline zichtbaar - field_watcher: Watcher + setting_file_max_size_displayed: Max grootte van tekstbestanden inline zichtbaar + field_watcher: Volger setting_openid: Sta OpenID login en registratie toe field_identity_url: OpenID URL - label_login_with_open_id_option: of login met je OpenID + label_login_with_open_id_option: of login met uw OpenID field_content: Content label_descending: Aflopend label_sort: Sorteer @@ -784,408 +783,423 @@ nl: label_date_from_to: Van %{start} tot %{end} label_greater_or_equal: ">=" label_less_or_equal: <= - text_wiki_page_destroy_question: Deze pagina heeft %{descendants} subpagina's en onderliggende pagina's?. Wat wilt u hiermee doen? + text_wiki_page_destroy_question: Deze pagina heeft %{descendants} subpagina's en onderliggende pagina's?. Wat wilt u doen? text_wiki_page_reassign_children: Alle subpagina's toewijzen aan deze hoofdpagina text_wiki_page_nullify_children: Behoud subpagina's als hoofdpagina's text_wiki_page_destroy_children: Verwijder alle subpagina's en onderliggende pagina's - setting_password_min_length: Minimum wachtwoord lengte + setting_password_min_length: Minimum wachtwoordlengte field_group_by: Groepeer resultaten per - mail_subject_wiki_content_updated: "'%{id}' wiki pagina is bijgewerkt" - label_wiki_content_added: Wiki pagina toegevoegd - mail_subject_wiki_content_added: "'%{id}' wiki pagina is toegevoegd" - mail_body_wiki_content_added: De '%{id}' wiki pagina is toegevoegd door %{author}. - label_wiki_content_updated: Wiki pagina bijgewerkt - mail_body_wiki_content_updated: De '%{id}' wiki pagina is bijgewerkt door %{author}. + mail_subject_wiki_content_updated: "'%{id}' wikipagina is bijgewerkt" + label_wiki_content_added: Wikipagina toegevoegd + mail_subject_wiki_content_added: "'%{id}' wikipagina is toegevoegd" + mail_body_wiki_content_added: De '%{id}' wikipagina is toegevoegd door %{author}. + label_wiki_content_updated: Wikipagina bijgewerkt + mail_body_wiki_content_updated: De '%{id}' wikipagina is bijgewerkt door %{author}. permission_add_project: Maak project setting_new_project_user_role_id: Rol van gebruiker die een project maakt - label_view_all_revisions: Bekijk alle revisies + label_view_all_revisions: Alle revisies bekijken label_tag: Tag label_branch: Branch - error_no_tracker_in_project: Geen tracker is geassocieerd met dit project. Check de project instellingen. - error_no_default_issue_status: Geen standaard issue status ingesteld. Check de configuratie (Ga naar "Administratie -> Issue statussen"). + error_no_tracker_in_project: Geen tracker is geassocieerd met dit project. Check de projectinstellingen. + error_no_default_issue_status: Geen standaard issuestatus ingesteld. Check de configuratie (Ga naar "Administratie -> Issuestatussen"). text_journal_changed: "%{label} gewijzigd van %{old} naar %{new}" text_journal_set_to: "%{label} gewijzigd naar %{value}" text_journal_deleted: "%{label} verwijderd (%{old})" label_group_plural: Groepen label_group: Groep label_group_new: Nieuwe groep - label_time_entry_plural: Bestede tijd + label_time_entry_plural: Tijdregistraties text_journal_added: "%{label} %{value} toegevoegd" field_active: Actief - enumeration_system_activity: Systeem Activiteit - permission_delete_issue_watchers: Verwijder volgers + enumeration_system_activity: Systeemactiviteit + permission_delete_issue_watchers: Volgers verwijderen version_status_closed: gesloten version_status_locked: vergrendeld version_status_open: open error_can_not_reopen_issue_on_closed_version: Een issue toegewezen aan een gesloten versie kan niet heropend worden label_user_anonymous: Anoniem - button_move_and_follow: Verplaats en volg + button_move_and_follow: Verplaatsen en volgen setting_default_projects_modules: Standaard geactiveerde modules voor nieuwe projecten setting_gravatar_default: Standaard Gravatar plaatje field_sharing: Delen - label_version_sharing_hierarchy: Met project hiërarchie + label_version_sharing_hierarchy: Met projecthiërarchie label_version_sharing_system: Met alle projecten label_version_sharing_descendants: Met subprojecten - label_version_sharing_tree: Met project boom + label_version_sharing_tree: Met projectboom label_version_sharing_none: Niet gedeeld error_can_not_archive_project: Dit project kan niet worden gearchiveerd button_duplicate: Dupliceer - button_copy_and_follow: Kopiëer en volg + button_copy_and_follow: Kopiëren en volgen label_copy_source: Bron - setting_issue_done_ratio: Bereken issue percentage voldaan met - setting_issue_done_ratio_issue_status: Gebruik de issue status - error_issue_done_ratios_not_updated: Issue percentage voldaan niet geupdate. + setting_issue_done_ratio: Bereken voltooiingspercentage voor issue met + setting_issue_done_ratio_issue_status: Gebruik de issuestatus + error_issue_done_ratios_not_updated: Issue-voltooiingspercentage niet gewijzigd. error_workflow_copy_target: Selecteer tracker(s) en rol(len) - setting_issue_done_ratio_issue_field: Gebruik het issue veld + setting_issue_done_ratio_issue_field: Gebruik het issue-veld label_copy_same_as_target: Zelfde als doel label_copy_target: Doel - notice_issue_done_ratios_updated: Issue percentage voldaan geupdate. - error_workflow_copy_source: Selecteer een bron tracker of rol - label_update_issue_done_ratios: Update issue percentage voldaan + notice_issue_done_ratios_updated: Issue-voltooiingspercentage aangepast. + error_workflow_copy_source: Selecteer een brontracker of rol + label_update_issue_done_ratios: Update issue-voltooiingspercentage setting_start_of_week: Week begint op - permission_view_issues: Bekijk Issues - label_display_used_statuses_only: Laat alleen statussen zien die gebruikt worden door deze tracker + permission_view_issues: Issues bekijken + label_display_used_statuses_only: Alleen statussen weergeven die gebruikt worden door deze tracker label_revision_id: Revisie %{value} - label_api_access_key: API access key - label_api_access_key_created_on: API access key gemaakt %{value} geleden - label_feeds_access_key: Atom access key - notice_api_access_key_reseted: Uw API access key was gereset. + label_api_access_key: API-toegangssleutel + label_api_access_key_created_on: "API-toegangssleutel %{value} geleden gemaakt" + label_feeds_access_key: Atom-toegangssleutel + notice_api_access_key_reseted: Uw API-toegangssleutel werd opnieuw ingesteld. setting_rest_api_enabled: Activeer REST web service - label_missing_api_access_key: Geen API access key - label_missing_feeds_access_key: Geen Atom access key - button_show: Laat zien + label_missing_api_access_key: Geen API-toegangssleutel + label_missing_feeds_access_key: Geen Atom-toegangssleutel + button_show: Weergeven text_line_separated: Meerdere waarden toegestaan (elke regel is een waarde). - setting_mail_handler_body_delimiters: Breek email verwerking af na een van deze regels - permission_add_subprojects: Maak subprojecten + setting_mail_handler_body_delimiters: E-mailverwerking afbreken na een van deze regels + permission_add_subprojects: Subprojecten aanmaken label_subproject_new: Nieuw subproject text_own_membership_delete_confirmation: |- - U staat op punt om sommige of alle van uw permissies te verwijderen en bent mogelijk niet meer toegestaan om dit project hierna te wijzigen. - Wilt u doorgaan? - label_close_versions: Sluit complete versies - label_board_sticky: Sticky + U staat op het punt om enkele van (of al) uw permissies te verwijderen, zodus het is mogelijk dat + u dit project hierna niet meer kan wijzigen. Wilt u doorgaan? + label_close_versions: Afgeronde versies sluiten + label_board_sticky: Vastgeplakt (sticky) label_board_locked: Vergrendeld - permission_export_wiki_pages: Exporteer wiki pagina's - setting_cache_formatted_text: Cache opgemaakte tekst - permission_manage_project_activities: Beheer project activiteiten - error_unable_delete_issue_status: Verwijderen van issue status niet gelukt + permission_export_wiki_pages: Wikipagina's exporteren + setting_cache_formatted_text: Opgemaakte tekst cachen + permission_manage_project_activities: Projectactiviteiten beheren + error_unable_delete_issue_status: Verwijderen van issuestatus is niet gelukt label_profile: Profiel - permission_manage_subtasks: Beheer subtaken - field_parent_issue: Hoofdtaak + permission_manage_subtasks: Subtaken beheren + field_parent_issue: Hoofdissue label_subtask_plural: Subtaken - label_project_copy_notifications: Stuur email notificaties voor de project kopie - error_can_not_delete_custom_field: Verwijderen niet mogelijk van custom field + label_project_copy_notifications: E-mailnotificaties voor de projectkopie sturen + error_can_not_delete_custom_field: Custom field verwijderen is niet mogelijk error_unable_to_connect: Geen connectie (%{value}) error_can_not_remove_role: Deze rol is in gebruik en kan niet worden verwijderd. - error_can_not_delete_tracker: Deze tracker bevat nog issues en kan niet worden verwijderd. + error_can_not_delete_tracker: Deze tracker bevat nog issues en kan niet verwijderd worden. field_principal: Hoofd - label_my_page_block: Mijn pagina block - notice_failed_to_save_members: "Niet gelukt om lid/leden op te slaan: %{errors}." - text_zoom_out: Zoom uit - text_zoom_in: Zoom in - notice_unable_delete_time_entry: Verwijderen niet mogelijk van tijd log invoer. - label_overall_spent_time: Totaal bestede tijd - field_time_entries: Registreer tijd + notice_failed_to_save_members: "Het is niet gelukt om lid/leden op te slaan: %{errors}." + text_zoom_out: Uitzoomen + text_zoom_in: Inzoomen + notice_unable_delete_time_entry: Verwijderen van tijdregistratie is niet mogelijk. + label_overall_spent_time: Totaal gespendeerde tijd + field_time_entries: Tijdregistratie project_module_gantt: Gantt project_module_calendar: Kalender - button_edit_associated_wikipage: "Bewerk bijbehorende wiki pagina: %{page_title}" - field_text: Tekst veld - label_user_mail_option_only_owner: Alleen voor dingen waarvan ik de auteur ben - setting_default_notification_option: Standaard instelling voor mededelingen - label_user_mail_option_only_my_events: Alleen voor dingen die ik volg of bij betrokken ben - label_user_mail_option_only_assigned: Alleen voor dingen die aan mij zijn toegewezen - label_user_mail_option_none: Bij geen enkele gebeurtenis - field_member_of_group: Groep van toegewezene - field_assigned_to_role: Rol van toegewezene + button_edit_associated_wikipage: "Bijbehorende wikipagina bewerken: %{page_title}" + field_text: Tekstveld + setting_default_notification_option: Standaardinstelling voor mededelingen + label_user_mail_option_only_my_events: Alleen voor activiteiten die ik volg of waarbij ik betrokken ben + label_user_mail_option_none: Bij geen enkele activiteit + field_member_of_group: Groep van toegewezen persoon + field_assigned_to_role: Rol van toegewezen persoon notice_not_authorized_archived_project: Het project dat u wilt bezoeken is gearchiveerd. label_principal_search: "Zoek naar gebruiker of groep:" label_user_search: "Zoek naar gebruiker:" - field_visible: Zichtbaar - setting_commit_logtime_activity_id: Standaard activiteit voor tijdregistratie + field_visible: Zichtbaarheid + setting_commit_logtime_activity_id: Standaardactiviteit voor tijdregistratie text_time_logged_by_changeset: Toegepast in changeset %{value}. - setting_commit_logtime_enabled: Activeer tijdregistratie - notice_gantt_chart_truncated: De gantt chart is ingekort omdat het meer objecten bevat dan kan worden weergegeven, (%{max}) - setting_gantt_items_limit: Max. aantal objecten op gantt chart - field_warn_on_leaving_unsaved: Waarschuw me wanneer ik een pagina verlaat waarvan de tekst niet opgeslagen is - text_warn_on_leaving_unsaved: De huidige pagina bevat tekst die niet is opgeslagen en dit zal verloren gaan als u deze pagina nu verlaat. + setting_commit_logtime_enabled: Tijdregistratie activeren + notice_gantt_chart_truncated: De Gantt-grafiek is ingekort omdat het meer objecten bevat dan kan worden weergegeven, (%{max}) + setting_gantt_items_limit: Max. aantal objecten op Gantt-grafiek + field_warn_on_leaving_unsaved: Waarschuw me wanneer ik een pagina verlaat waarvan de tekst niet is opgeslagen + text_warn_on_leaving_unsaved: De huidige pagina bevat tekst die niet is opgeslagen en zal verloren gaan als u deze pagina nu verlaat. label_my_queries: Mijn aangepaste zoekopdrachten - text_journal_changed_no_detail: "%{label} updated" - label_news_comment_added: Commentaar toegevoegd aan een nieuwsitem - button_expand_all: Klap uit - button_collapse_all: Klap in - label_additional_workflow_transitions_for_assignee: Aanvullende veranderingen toegestaan wanneer de gebruiker de toegewezene is + text_journal_changed_no_detail: "%{label} gewijzigd" + label_news_comment_added: Commentaar aan een nieuwsitem toegevoegd + button_expand_all: Uitklappen + button_collapse_all: Inklappen + label_additional_workflow_transitions_for_assignee: Aanvullende veranderingen toegestaan wanneer de gebruiker de toegewezen persoon is label_additional_workflow_transitions_for_author: Aanvullende veranderingen toegestaan wanneer de gebruiker de auteur is label_bulk_edit_selected_time_entries: Alle geselecteerde tijdregistraties wijzigen? - text_time_entries_destroy_confirmation: Weet u zeker dat u de geselecteerde item(s) wilt verwijderen ? + text_time_entries_destroy_confirmation: Weet u zeker dat u de geselecteerde tijdregistratie(s) wilt verwijderen ? label_role_anonymous: Anoniem label_role_non_member: Geen lid label_issue_note_added: Notitie toegevoegd label_issue_status_updated: Status gewijzigd label_issue_priority_updated: Prioriteit gewijzigd - label_issues_visibility_own: Issue aangemaakt door of toegewezen aan - field_issues_visibility: Issues weergave + label_issues_visibility_own: Issues aangemaakt door of toegewezen aan de gebruiker + field_issues_visibility: Issueweergave label_issues_visibility_all: Alle issues - permission_set_own_issues_private: Zet eigen issues publiekelijk of privé + permission_set_own_issues_private: Eigen issues openbaar of privé maken field_is_private: Privé - permission_set_issues_private: Zet issues publiekelijk of privé + permission_set_issues_private: Issues openbaar of privé maken label_issues_visibility_public: Alle niet privé-issues - text_issues_destroy_descendants_confirmation: Dit zal ook de %{count} subtaken verwijderen. - field_commit_logs_encoding: Encodering van commit berichten - field_scm_path_encoding: Pad encodering + text_issues_destroy_descendants_confirmation: Dit zal ook %{count} subtaken verwijderen. + field_commit_logs_encoding: Codering van commitberichten + field_scm_path_encoding: Padcodering text_scm_path_encoding_note: "Standaard: UTF-8" - field_path_to_repository: Pad naar versie overzicht - field_root_directory: Root directory + field_path_to_repository: Pad naar versieoverzicht + field_root_directory: Hoofdmap field_cvs_module: Module field_cvsroot: CVSROOT - text_mercurial_repository_note: "Lokale versie overzicht (Voorbeeld: /hgrepo, c:\\hgrepo)" + text_mercurial_repository_note: "Lokale versieoverzicht (Voorbeeld: /hgrepo, c:\\hgrepo)" text_scm_command: Commando text_scm_command_version: Versie - label_git_report_last_commit: Rapporteer laatste toevoegen voor bestanden en directories - text_scm_config: U kan de scm commando's configureren in config/configuration.yml. Herstart de applicatie na het wijzigen ervan. - text_scm_command_not_available: Scm commando is niet beschikbaar. Controleer de instellingen in het administratiepaneel. + label_git_report_last_commit: Laatste toevoegen voor bestanden en mappen rapporteren + text_scm_config: U kan de SCM-commando's instellen in config/configuration.yml. U moet de applicatie herstarten na de wijzigingen. + text_scm_command_not_available: SCM-commando is niet beschikbaar. Controleer de instellingen in het administratiepaneel. notice_issue_successful_create: Issue %{id} aangemaakt. label_between: tussen - setting_issue_group_assignment: Sta groepstoewijzingen toe + setting_issue_group_assignment: Groepstoewijzingen toelaten label_diff: diff - text_git_repository_note: "Versie overzicht lokaal is leeg (Voorbeeld: /gitrepo, c:\\gitrepo)" + text_git_repository_note: "Lokaal versieoverzicht is leeg (Voorbeeld: /gitrepo, c:\\gitrepo)" description_query_sort_criteria_direction: Sortering - description_project_scope: Zoek bereik - description_filter: Filter - description_user_mail_notification: Mail notificatie instellingen - description_date_from: Vul start datum in - description_message_content: Inhoud bericht + description_project_scope: Zoekbereik + description_filter: Filteren + description_user_mail_notification: Instellingen voor e-mailnotificaties + description_message_content: Berichtinhoud description_available_columns: Beschikbare kolommen - description_date_range_interval: Kies een bereik bij het selecteren van een start en eind datum - description_issue_category_reassign: Kies issue categorie + description_issue_category_reassign: Issuecategorie kiezen description_search: Zoekveld description_notes: Notities - description_date_range_list: Kies bereik vanuit de lijst description_choose_project: Projecten - description_date_to: Vul eind datum in - description_query_sort_criteria_attribute: Sorteer attribuut - description_wiki_subpages_reassign: Kies nieuwe hoofdpagina + description_query_sort_criteria_attribute: Attribuut sorteren + description_wiki_subpages_reassign: Nieuwe hoofdpagina kiezen description_selected_columns: Geselecteerde kolommen label_parent_revision: Hoofd label_child_revision: Sub error_scm_annotate_big_text_file: De vermelding kan niet worden geannoteerd, omdat het groter is dan de maximale toegewezen grootte. - setting_default_issue_start_date_to_creation_date: Gebruik huidige datum als start datum voor nieuwe issues. - button_edit_section: Wijzig deze sectie - setting_repositories_encodings: Bijlage en opgeslagen bestanden coderingen + setting_default_issue_start_date_to_creation_date: Huidige datum als startdatum gebruiken voor nieuwe issues. + button_edit_section: Deze sectie wijzigen + setting_repositories_encodings: Coderingen voor bijlagen en opgeslagen bestanden description_all_columns: Alle kolommen button_export: Exporteren label_export_options: "%{export_format} export opties" - error_attachment_too_big: Dit bestand kan niet worden geupload omdat het de maximaal toegestane grootte overschrijd (%{max_size}) - notice_failed_to_save_time_entries: "Opslaan gefaald voor %{count} tijdsnotatie(s) van %{total} geselecteerde: %{ids}." + error_attachment_too_big: Dit bestand kan niet worden geüpload omdat het de maximaal toegestane grootte overschrijdt (%{max_size}) + notice_failed_to_save_time_entries: "Opslaan mislukt voor %{count} tijdregistratie(s) van %{total} geselecteerde: %{ids}." label_x_issues: zero: 0 issues one: 1 issue other: "%{count} issues" - label_repository_new: Nieuw repository - field_repository_is_default: Hoofd repository + label_repository_new: Nieuwe repository + field_repository_is_default: Hoofdrepository label_copy_attachments: Kopieer bijlage(n) label_item_position: "%{position}/%{count}" - label_completed_versions: Versies compleet - field_multiple: Meerdere waardes - setting_commit_cross_project_ref: Sta toe om issues van alle projecten te refereren en oplossen - text_issue_conflict_resolution_add_notes: Voeg mijn notities toe en annuleer andere wijzigingen - text_issue_conflict_resolution_overwrite: Voeg mijn wijzigingen alsnog toe (voorgaande notities worden bewaard, maar sommige kunnen overschreden worden) - notice_issue_update_conflict: Dit issue is reeds geupdate door een andere gebruiker terwijl jij bezig was - text_issue_conflict_resolution_cancel: Annuleer mijn wijzigingen en geef pagina opnieuw weer %{link} - permission_manage_related_issues: Beheer gerelateerde issues + label_completed_versions: Afgeronde versies + field_multiple: Meerdere waarden + setting_commit_cross_project_ref: Toestaan om issues van alle projecten te refereren en op te lossen + text_issue_conflict_resolution_add_notes: Mijn notities toevoegen en andere wijzigingen annuleren + text_issue_conflict_resolution_overwrite: Mijn wijzigingen alsnog toevoegen (voorgaande notities worden opgeslagen, + maar sommige notities kunnen overschreden worden) + notice_issue_update_conflict: Dit issue is reeds aangepast door een andere gebruiker terwijl u bezig was met wijzigingen + aan te brengen + text_issue_conflict_resolution_cancel: "Mijn wijzigingen annuleren en pagina opnieuw weergeven: %{link}" + permission_manage_related_issues: Gerelateerde issues beheren field_auth_source_ldap_filter: LDAP filter - label_search_for_watchers: Zoek om monitors toe te voegen + label_search_for_watchers: Klik om volgers toe te voegen notice_account_deleted: Uw account is permanent verwijderd - setting_unsubscribe: Sta gebruikers toe hun eigen account te verwijderen - button_delete_my_account: Verwijder mijn account + setting_unsubscribe: Gebruikers toestaan hun eigen account te verwijderen + button_delete_my_account: Mijn account verwijderen text_account_destroy_confirmation: |- Weet u zeker dat u door wilt gaan? - Uw account wordt permanent verwijderd zonder mogelijkheid deze te heractiveren. - error_session_expired: Uw sessie is verlopen. U dient opnieuw in te loggen. - text_session_expiration_settings: "Waarschuwing: door deze instelling te wijzigen kan sessies laten verlopen inclusief de uwe" + Uw account wordt permanent verwijderd zonder enige mogelijkheid deze te heractiveren. + error_session_expired: Uw sessie is verlopen. Gelieve opnieuw in te loggen. + text_session_expiration_settings: "Opgelet: door het wijzigen van deze instelling zullen de huidige sessies verlopen, inclusief die van u." setting_session_lifetime: Maximale sessieduur - setting_session_timeout: Sessie inactiviteit timeout + setting_session_timeout: Sessie-inactiviteit timeout label_session_expiration: Sessie verlopen - permission_close_project: Sluit / heropen project + permission_close_project: Project sluiten/heropenen label_show_closed_projects: Gesloten projecten weergeven button_close: Sluiten - button_reopen: Heropen + button_reopen: Heropenen project_status_active: actief project_status_closed: gesloten project_status_archived: gearchiveerd - text_project_closed: Dit project is gesloten en op alleen-lezen + text_project_closed: Dit project is gesloten en kan alleen gelezen worden notice_user_successful_create: Gebruiker %{id} aangemaakt. - field_core_fields: Standaard verleden + field_core_fields: Standaardvelden field_timeout: Timeout (in seconden) - setting_thumbnails_enabled: Geef bijlage miniaturen weer + setting_thumbnails_enabled: Miniaturen voor bijlagen weergeven setting_thumbnails_size: Grootte miniaturen (in pixels) - label_status_transitions: Status transitie - label_fields_permissions: Permissie velden + label_status_transitions: Statustransitie + label_fields_permissions: Permissievelden label_readonly: Alleen-lezen label_required: Verplicht text_repository_identifier_info: 'Alleen kleine letter (a-z), cijfers, streepjes en liggende streepjes zijn toegestaan.
    Eenmaal opgeslagen kan de identifier niet worden gewijzigd.' - field_board_parent: Hoofd forum + field_board_parent: Hoofdforum label_attribute_of_project: Project %{name} label_attribute_of_author: Auteur(s) %{name} label_attribute_of_assigned_to: Toegewezen %{name} - label_attribute_of_fixed_version: Target versions %{name} - label_copy_subtasks: Kopieer subtaken + label_attribute_of_fixed_version: "%{name} van versie" + label_copy_subtasks: Subtaken kopiëren label_copied_to: gekopieerd naar label_copied_from: gekopieerd van - label_any_issues_in_project: any issues in project - label_any_issues_not_in_project: any issues not in project - field_private_notes: Privé notities - permission_view_private_notes: Bekijk privé notities - permission_set_notes_private: Maak notities privé + label_any_issues_in_project: alle issues in project + label_any_issues_not_in_project: alle issues niet in project + field_private_notes: Privénotities + permission_view_private_notes: Privénotities bekijken + permission_set_notes_private: Notities privé maken label_no_issues_in_project: geen issues in project label_any: alle label_last_n_weeks: afgelopen %{count} weken - setting_cross_project_subtasks: Sta subtaken in andere projecten toe + setting_cross_project_subtasks: Subtaken in andere projecten toelaten label_cross_project_descendants: Met subprojecten - label_cross_project_tree: Met project boom - label_cross_project_hierarchy: Met project hiërarchie + label_cross_project_tree: Met projectboom + label_cross_project_hierarchy: Met projecthiërarchie label_cross_project_system: Met alle projecten button_hide: Verberg setting_non_working_week_days: Niet-werkdagen label_in_the_next_days: in de volgende label_in_the_past_days: in de afgelopen - label_attribute_of_user: User's %{name} - text_turning_multiple_off: Bij het uitschakelen van meerdere waardes zal er maar een waarde bewaard blijven. - label_attribute_of_issue: Issue's %{name} - permission_add_documents: Voeg documenten toe - permission_edit_documents: Bewerk documenten - permission_delete_documents: Verwijder documenten + label_attribute_of_user: Gebruikers %{name} + text_turning_multiple_off: "Bij het uitschakelen van meerdere waarden zal er maar één waarde bewaard blijven." + label_attribute_of_issue: Issues %{name} + permission_add_documents: Documenten toevoegen + permission_edit_documents: Documenten bewerken + permission_delete_documents: Documenten verwijderen label_gantt_progress_line: Voortgangslijn - setting_jsonp_enabled: Schakel JSONP support in - field_inherit_members: Neem leden over + setting_jsonp_enabled: JSONP support inschakelen + field_inherit_members: Bovenliggende leden erven field_closed_on: Gesloten - field_generate_password: Genereer wachtwoord - setting_default_projects_tracker_ids: Standaard trackers voor nieuwe projecten + field_generate_password: Wachtwoord genereren + setting_default_projects_tracker_ids: Standaardtrackers voor nieuwe projecten label_total_time: Totaal - setting_emails_header: Email header - notice_account_not_activated_yet: Je hebt je account nog niet geactiveerd. Om een nieuwe activatie email te ontvangen, klik op deze link. - notice_account_locked: Je account is vergrendeld. - notice_account_register_done: "Account aanmaken is gelukt. Een email met instructies om je account te activeren is gestuurd naar: %{email}." + setting_emails_header: E-mailhoofding + notice_account_not_activated_yet: U heeft uw account nog niet geactiveerd. Klik op deze link + om een nieuwe activeringsemail te versturen. + notice_account_locked: Uw account is vergrendeld. + notice_account_register_done: "Account aanmaken is gelukt. Een e-mail met instructies om uw account te activeren is verstuurd naar: %{email}." label_hidden: Verborgen label_visibility_private: voor mij alleen label_visibility_roles: alleen voor deze rollen label_visibility_public: voor elke gebruiker - field_must_change_passwd: Moet wachtwoord wijziging bij volgende keer inloggen + field_must_change_passwd: Wachtwoord wijzigen bij eerstvolgende login notice_new_password_must_be_different: Het nieuwe wachtwoord mag niet hetzelfde zijn als het huidige wachtwoord - setting_mail_handler_excluded_filenames: Exclude attachments by name - text_convert_available: ImageMagick convert beschikbaar (optioneel) + setting_mail_handler_excluded_filenames: Bijlagen uitsluiten op basis van naam + text_convert_available: ImageMagick comversie beschikbaar (optioneel) label_link: Link - label_only: only - label_drop_down_list: drop-down list - label_checkboxes: checkboxes - label_link_values_to: Link values to URL - setting_force_default_language_for_anonymous: Force default language for anonymous - users - setting_force_default_language_for_loggedin: Force default language for logged-in - users - label_custom_field_select_type: Select the type of object to which the custom field - is to be attached - label_issue_assigned_to_updated: Assignee updated - label_check_for_updates: Check for updates - label_latest_compatible_version: Latest compatible version - label_unknown_plugin: Unknown plugin - label_radio_buttons: radio buttons - label_group_anonymous: Anonymous users - label_group_non_member: Non member users - label_add_projects: Add projects - field_default_status: Default status - text_subversion_repository_note: 'Examples: file:///, http://, https://, svn://, svn+[tunnelscheme]://' - field_users_visibility: Users visibility - label_users_visibility_all: All active users - label_users_visibility_members_of_visible_projects: Members of visible projects - label_edit_attachments: Edit attached files - setting_link_copied_issue: Link issues on copy - label_link_copied_issue: Link copied issue - label_ask: Ask - label_search_attachments_yes: Search attachment filenames and descriptions - label_search_attachments_no: Do not search attachments - label_search_attachments_only: Search attachments only - label_search_open_issues_only: Open issues only - field_address: E-mail - setting_max_additional_emails: Maximum number of additional email addresses - label_email_address_plural: Emails - label_email_address_add: Add email address - label_enable_notifications: Enable notifications - label_disable_notifications: Disable notifications - setting_search_results_per_page: Search results per page - label_blank_value: blank - permission_copy_issues: Copy issues - error_password_expired: Your password has expired or the administrator requires you - to change it. - field_time_entries_visibility: Time logs visibility - setting_password_max_age: Require password change after - label_parent_task_attributes: Parent tasks attributes - label_parent_task_attributes_derived: Calculated from subtasks - label_parent_task_attributes_independent: Independent of subtasks - label_time_entries_visibility_all: All time entries - label_time_entries_visibility_own: Time entries created by the user - label_member_management: Member management - label_member_management_all_roles: All roles - label_member_management_selected_roles_only: Only these roles - label_password_required: Confirm your password to continue - label_total_spent_time: Totaal bestede tijd - notice_import_finished: "%{count} items have been imported" - notice_import_finished_with_errors: "%{count} out of %{total} items could not be imported" - error_invalid_file_encoding: The file is not a valid %{encoding} encoded file - error_invalid_csv_file_or_settings: The file is not a CSV file or does not match the - settings below - error_can_not_read_import_file: An error occurred while reading the file to import - permission_import_issues: Import issues - label_import_issues: Import issues - label_select_file_to_import: Select the file to import - label_fields_separator: Field separator - label_fields_wrapper: Field wrapper - label_encoding: Encoding - label_comma_char: Comma - label_semi_colon_char: Semicolon - label_quote_char: Quote - label_double_quote_char: Double quote - label_fields_mapping: Fields mapping - label_file_content_preview: File content preview - label_create_missing_values: Create missing values - button_import: Import - field_total_estimated_hours: Total estimated time + label_only: enkel + label_drop_down_list: keuzelijst + label_checkboxes: keuzevakjes + label_link_values_to: Waarden koppelen aan URL + setting_force_default_language_for_anonymous: Standaardtaal voor anonieme gebruikers + setting_force_default_language_for_loggedin: Standaardtaal voor ingelogde gebruikers + label_custom_field_select_type: Selecteer het objecttype waaraan u het vrij veld wilt vasthangen + label_issue_assigned_to_updated: Toegewezen persoon gewijzigd + label_check_for_updates: Controleren of er updates beschikbaar zijn + label_latest_compatible_version: Laatste compatibele versie + label_unknown_plugin: Onbekende plugin + label_radio_buttons: selectieknoppen + label_group_anonymous: Anonieme gebruikers + label_group_non_member: Geen lid gebruikers + label_add_projects: Projecten toevoegen + field_default_status: Standaardstatus + text_subversion_repository_note: 'Voorbeelden: file:///, http://, https://, svn://, svn+[tunnelschema]://' + field_users_visibility: Gebruikersweergave + label_users_visibility_all: Alle actieve gebruikers + label_users_visibility_members_of_visible_projects: Gebruikers van zichtbare projecten + label_edit_attachments: Bijlagen bewerken + setting_link_copied_issue: Koppelen met issue bij kopiëren + label_link_copied_issue: Koppelen met issuekopie + label_ask: Vraag iedere keer + label_search_attachments_yes: Zoeken in bestandsnamen en beschrijvingen van bijlagen + label_search_attachments_no: Niet zoeken in bijlagen + label_search_attachments_only: Enkel zoeken in bijlagen + label_search_open_issues_only: Enkel openstaande issues + field_address: Adres + setting_max_additional_emails: Maximum aantal bijkomende e-mailadressen + label_email_address_plural: E-mails + label_email_address_add: Nieuw e-mailadres + label_enable_notifications: Notificaties inschakelen + label_disable_notifications: Notificaties uitschakelen + setting_search_results_per_page: Zoekresultaten per pagina + label_blank_value: leeg + permission_copy_issues: Issues kopiëren + error_password_expired: Uw wachtwoord is verlopen of moet gewijzigd worden op vraag van een beheerder. + field_time_entries_visibility: Tijdregistratieweergave + setting_password_max_age: Wachtwoord wijzigen verplicht na + label_parent_task_attributes: Attributen van bovenliggende taken + label_parent_task_attributes_derived: Berekend vanuit subtaken + label_parent_task_attributes_independent: Onafhankelijk van subtaken + label_time_entries_visibility_all: Alle tijdregistraties + label_time_entries_visibility_own: Tijdregistraties aangemaakt door gebruiker + label_member_management: Ledenbeheer + label_member_management_all_roles: Alle rollen + label_member_management_selected_roles_only: Enkel deze rollen + label_password_required: Bevestig uw wachtwoord om verder te gaan + label_total_spent_time: Totaal gespendeerde tijd + notice_import_finished: "%{count} items werden geïmporteerd" + notice_import_finished_with_errors: "%{count} van in totaal %{total} items kunnen niet geïmporteerd worden" + error_invalid_file_encoding: Het bestand is geen geldig geëncodeerd %{encoding} bestand + error_invalid_csv_file_or_settings: Het bestand is geen CSV-bestand of voldoet niet aan onderstaande instellingen + error_can_not_read_import_file: Er is een fout opgetreden bij het inlezen van het bestand + permission_import_issues: Issues importeren + label_import_issues: Issues importeren + label_select_file_to_import: Selecteer het bestand om te importeren + label_fields_separator: Scheidingsteken + label_fields_wrapper: Tekstscheidingsteken + label_encoding: Codering + label_comma_char: Komma + label_semi_colon_char: Puntkomma + label_quote_char: Enkel aanhalingsteken + label_double_quote_char: Dubbel aanhalingsteken + label_fields_mapping: Veldkoppeling + label_file_content_preview: Voorbeeld van bestandsinhoud + label_create_missing_values: Ontbrekende waarden invullen + button_import: Importeren + field_total_estimated_hours: Geschatte totaaltijd label_api: API - label_total_plural: Totals - label_assigned_issues: Assigned issues - label_field_format_enumeration: Key/value list - label_f_hour_short: '%{value} h' - field_default_version: Default version - error_attachment_extension_not_allowed: Attachment extension %{extension} is not allowed - setting_attachment_extensions_allowed: Allowed extensions - setting_attachment_extensions_denied: Disallowed extensions - label_any_open_issues: any open issues - label_no_open_issues: no open issues - label_default_values_for_new_users: Default values for new users - error_ldap_bind_credentials: Invalid LDAP Account/Password - setting_sys_api_key: API sleutel - setting_lost_password: Wachtwoord verloren - mail_subject_security_notification: Security notification - mail_body_security_notification_change: ! '%{field} was changed.' - mail_body_security_notification_change_to: ! '%{field} was changed to %{value}.' - mail_body_security_notification_add: ! '%{field} %{value} was added.' - mail_body_security_notification_remove: ! '%{field} %{value} was removed.' - mail_body_security_notification_notify_enabled: Email address %{value} now receives - notifications. - mail_body_security_notification_notify_disabled: Email address %{value} no longer - receives notifications. - mail_body_settings_updated: ! 'The following settings were changed:' - field_remote_ip: IP address - label_wiki_page_new: New wiki page - label_relations: Relations - button_filter: Filter - mail_body_password_updated: Your password has been changed. - label_no_preview: No preview available - error_no_tracker_allowed_for_new_issue_in_project: The project doesn't have any trackers - for which you can create an issue - label_tracker_all: All trackers - label_new_project_issue_tab_enabled: Display the "New issue" tab - setting_new_item_menu_tab: Project menu tab for creating new objects - label_new_object_tab_enabled: Display the "+" drop-down - error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers - for which you can create an issue + label_total_plural: Totalen + label_assigned_issues: Toegewezen issues + label_field_format_enumeration: Sleutel/waarde lijst + label_f_hour_short: '%{value} u' + field_default_version: Standaardversie + error_attachment_extension_not_allowed: Bestandsextensie %{extension} van bijlage is niet toegelaten + setting_attachment_extensions_allowed: Toegelaten bestandsextensies + setting_attachment_extensions_denied: Niet toegelaten bestandsextensies + label_any_open_issues: alle open issues + label_no_open_issues: geen open issues + label_default_values_for_new_users: Standaardwaarden voor nieuwe gebruikers + error_ldap_bind_credentials: Ongeldige LDAP account/wachtwoord-combinatie + setting_sys_api_key: API-sleutel + setting_lost_password: Wachtwoord vergeten + mail_subject_security_notification: Beveiligingsnotificatie + mail_body_security_notification_change: ! '%{field} werd aangepast.' + mail_body_security_notification_change_to: ! '%{field} werd aangepast naar %{value}.' + mail_body_security_notification_add: ! '%{field} %{value} werd toegevoegd.' + mail_body_security_notification_remove: ! '%{field} %{value} werd verwijderd.' + mail_body_security_notification_notify_enabled: E-mailadres %{value} ontvangt vanaf heden notificaties. + mail_body_security_notification_notify_disabled: E-mailadres %{value} ontvangt niet langer notificaties. + mail_body_settings_updated: ! 'Volgende instellingen werden gewijzigd:' + field_remote_ip: IP-adres + label_wiki_page_new: Nieuwe wikipagina + label_relations: Relaties + button_filter: Filteren + mail_body_password_updated: Uw wachtwoord werd gewijzigd. + label_no_preview: Geen voorbeeld beschikbaar + error_no_tracker_allowed_for_new_issue_in_project: Het project bevat geen tracker waarvoor u een + issue kan aanmaken + label_tracker_all: Alle trackers + setting_new_item_menu_tab: Projectmenu-tab om nieuwe objecten aan te maken + label_new_project_issue_tab_enabled: ! '"Nieuw issue"-tab weergeven' + label_new_object_tab_enabled: ! '"+"-tab met keuzelijst weergeven' + error_no_projects_with_tracker_allowed_for_new_issue: Er zijn geen projecten met trackers + waarvoor u een issue kan aanmaken + field_textarea_font: Lettertype voor tekstvelden + label_font_default: Standaardlettertype + label_font_monospace: Monospaced-lettertype + label_font_proportional: Proportioneel lettertype + setting_timespan_format: Tijdspanneformaat + label_table_of_contents: Inhoudsopgave + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/no.yml b/config/locales/no.yml index 9a02d7b30..54628bad3 100644 --- a/config/locales/no.yml +++ b/config/locales/no.yml @@ -119,6 +119,8 @@ circular_dependency: "Denne relasjonen ville lagd en sirkulær avhengighet" cant_link_an_issue_with_a_descendant: "En sak kan ikke kobles mot en av sine undersaker" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" actionview_instancetag_blank_option: Vennligst velg @@ -440,7 +442,6 @@ label_internal: Intern label_last_changes: "siste %{count} endringer" label_change_view_all: Vis alle endringer - label_personalize_page: Tilpass denne siden label_comment: Kommentar label_comment_plural: Kommentarer label_x_comments: @@ -584,7 +585,6 @@ label_age: Alder label_change_properties: Endre egenskaper label_general: Generell - label_more: Mer label_scm: SCM label_plugins: Tillegg label_ldap_authentication: LDAP-autentisering @@ -594,7 +594,6 @@ label_preferences: Brukerinnstillinger label_chronological_order: I kronologisk rekkefølge label_reverse_chronological_order: I omvendt kronologisk rekkefølge - label_planning: Planlegging button_login: Logg inn button_submit: Send @@ -883,7 +882,6 @@ error_can_not_remove_role: Denne rollen er i bruk og kan ikke slettes. error_can_not_delete_tracker: Denne sakstypen inneholder saker og kan ikke slettes. field_principal: Principal - label_my_page_block: Min side felt notice_failed_to_save_members: "Feil ved lagring av medlem(mer): %{errors}." text_zoom_out: Zoom ut text_zoom_in: Zoom inn @@ -894,10 +892,8 @@ project_module_calendar: Kalender button_edit_associated_wikipage: "Rediger tilhørende Wiki-side: %{page_title}" field_text: Tekstfelt - label_user_mail_option_only_owner: Kun for ting jeg eier setting_default_notification_option: Standardvalg for varslinger label_user_mail_option_only_my_events: Kun for ting jeg overvåker eller er involvert i - label_user_mail_option_only_assigned: Kun for ting jeg er tildelt label_user_mail_option_none: Ingen hendelser field_member_of_group: Den tildeltes gruppe field_assigned_to_role: Den tildeltes rolle @@ -960,16 +956,12 @@ description_project_scope: Search scope description_filter: Filter description_user_mail_notification: Mail notification settings - description_date_from: Oppgi startdato description_message_content: Meldingsinnhold description_available_columns: Tilgjengelige kolonner - description_date_range_interval: Velg datointervall ved å spesifisere start- og sluttdato description_issue_category_reassign: Choose issue category description_search: Søkefelt description_notes: Notes - description_date_range_list: Choose range from list description_choose_project: Prosjekter - description_date_to: Oppgi sluttdato description_query_sort_criteria_attribute: Sort attribute description_wiki_subpages_reassign: Velg ny overordnet side description_selected_columns: Valgte kolonner @@ -1198,5 +1190,31 @@ label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/pl.yml b/config/locales/pl.yml index ecbbc9f66..81ab1e5f7 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -138,6 +138,8 @@ pl: circular_dependency: "Ta relacja może wytworzyć zapętloną zależność" cant_link_an_issue_with_a_descendant: "Zagadnienie nie może zostać powiązane z jednym z własnych podzagadnień" earlier_than_minimum_start_date: "nie może być wcześniej niż %{date} z powodu poprzedających zagadnień" + 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" support: array: @@ -492,7 +494,6 @@ pl: label_module_plural: Moduły label_month: Miesiąc label_months_from: miesiące od - label_more: Więcej label_more_than_ago: dni od teraz label_my_account: Moje konto label_my_page: Moja strona @@ -523,8 +524,6 @@ pl: label_password_lost: Zapomniane hasło label_permissions: Uprawnienia label_permissions_report: Raport uprawnień - label_personalize_page: Personalizuj tę stronę - label_planning: Planowanie label_please_login: Zaloguj się label_plugins: Wtyczki label_precedes: poprzedza @@ -915,7 +914,6 @@ pl: error_can_not_remove_role: Ta rola przypisana jest niektórym użytkownikom i nie może zostać usunięta. error_can_not_delete_tracker: Ten typ przypisany jest do części zagadnień i nie może zostać usunięty. field_principal: Przełożony - label_my_page_block: Elementy notice_failed_to_save_members: "Nie można zapisać uczestników: %{errors}." text_zoom_out: Zmniejsz text_zoom_in: Powiększ @@ -926,10 +924,8 @@ pl: project_module_calendar: Kalendarz button_edit_associated_wikipage: "Edytuj powiązaną stronę Wiki: %{page_title}" field_text: Text field - label_user_mail_option_only_owner: Tylko to, czego jestem właścicielem (autorem) setting_default_notification_option: Domyślna opcja powiadomień label_user_mail_option_only_my_events: "Tylko to, co obserwuję lub w czym biorę udział" - label_user_mail_option_only_assigned: "Tylko to, do czego jestem przypisany" label_user_mail_option_none: "Brak powiadomień" field_member_of_group: Grupa osoby przypisanej field_assigned_to_role: Rola osoby przypisanej @@ -986,16 +982,12 @@ pl: description_project_scope: Search scope description_filter: Filter description_user_mail_notification: Mail notification settings - description_date_from: Enter start date description_message_content: Message content description_available_columns: Dostępne kolumny - description_date_range_interval: Choose range by selecting start and end date description_issue_category_reassign: Choose issue category description_search: Searchfield description_notes: Notes - description_date_range_list: Choose range from list description_choose_project: Projects - description_date_to: Enter end date description_query_sort_criteria_attribute: Sort attribute description_wiki_subpages_reassign: Choose new parent page description_selected_columns: Wybrane kolumny @@ -1223,5 +1215,31 @@ pl: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index 2e2e159cc..3f190ee62 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -148,8 +148,10 @@ pt-BR: greater_than_start_date: "deve ser maior que a data inicial" not_same_project: "não pertence ao mesmo projeto" circular_dependency: "Esta relação geraria uma dependência circular" - cant_link_an_issue_with_a_descendant: "Uma tarefa não pode ser relaciona a uma de suas subtarefas" - earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + cant_link_an_issue_with_a_descendant: "Uma tarefa não pode ser relacionada a uma de suas subtarefas" + earlier_than_minimum_start_date: "não pode ser anterior a %{date} por causa de tarefas anteriores" + not_a_regexp: "não é uma expressão regular válida" + open_issue_with_closed_parent: "Uma tarefa aberta não pode ser associada à uma tarefa pai fechada" actionview_instancetag_blank_option: Selecione @@ -157,7 +159,7 @@ pt-BR: general_text_Yes: 'Sim' general_text_no: 'não' 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_decimal_separator: ',' general_csv_encoding: ISO-8859-1 @@ -248,7 +250,7 @@ pt-BR: field_role: Cargo field_homepage: Página do projeto field_is_public: Público - field_parent: Sub-projeto de + field_parent: Subprojeto de field_is_in_roadmap: Exibir no planejamento field_login: Usuário field_mail_notification: Notificações por e-mail @@ -311,14 +313,14 @@ pt-BR: setting_feeds_limit: Número de registros por Feed setting_default_projects_public: Novos projetos são públicos por padrão setting_autofetch_changesets: Obter commits automaticamente - setting_sys_api_enabled: Ativa WS para gerenciamento do repositório (SVN) + setting_sys_api_enabled: Ativar WS para gerenciamento do repositório (SVN) setting_commit_ref_keywords: Palavras-chave de referência setting_commit_fix_keywords: Definição de palavras-chave setting_autologin: Auto-login setting_date_format: Formato da data setting_time_format: Formato de hora setting_cross_project_issue_relations: Permitir relacionar tarefas entre projetos - setting_issue_list_default_columns: Colunas padrão visíveis na lista de tarefas + setting_issue_list_default_columns: Colunas na lista de tarefas por padrão setting_emails_footer: Rodapé do e-mail setting_protocol: Protocolo setting_per_page_options: Número de itens exibidos por página @@ -420,7 +422,7 @@ pt-BR: label_date: Data label_integer: Inteiro label_float: Decimal - label_boolean: Boleano + label_boolean: Booleano label_string: Texto label_text: Texto longo label_attribute: Atributo @@ -480,7 +482,6 @@ pt-BR: label_internal: Interno label_last_changes: "últimas %{count} alterações" label_change_view_all: Mostrar todas as alterações - label_personalize_page: Personalizar esta página label_comment: Comentário label_comment_plural: Comentários label_x_comments: @@ -624,7 +625,6 @@ pt-BR: label_age: Idade label_change_properties: Alterar propriedades label_general: Geral - label_more: Mais label_scm: 'Controle de versão:' label_plugins: Plugins label_ldap_authentication: Autenticação LDAP @@ -634,7 +634,6 @@ pt-BR: label_preferences: Preferências label_chronological_order: Em ordem cronológica label_reverse_chronological_order: Em ordem cronológica inversa - label_planning: Planejamento label_incoming_emails: E-mails recebidos label_generate_key: Gerar uma chave label_issue_watchers: Observadores @@ -864,7 +863,7 @@ pt-BR: field_sharing: Compartilhamento label_version_sharing_hierarchy: Com a hierarquia do projeto label_version_sharing_system: Com todos os projetos - label_version_sharing_descendants: Com sub-projetos + label_version_sharing_descendants: Com subprojetos label_version_sharing_tree: Com a árvore do projeto label_version_sharing_none: Sem compartilhamento error_can_not_archive_project: Este projeto não pode ser arquivado @@ -886,13 +885,13 @@ pt-BR: permission_view_issues: Ver tarefas label_display_used_statuses_only: Somente exibir situações que são usadas por este tipo de tarefa label_revision_id: Revisão %{value} - label_api_access_key: Chave de acesso a API + label_api_access_key: Chave de acesso à API button_show: Exibir - label_api_access_key_created_on: Chave de acesso a API criado a %{value} atrás + label_api_access_key_created_on: Chave de acesso à API criado há %{value} atrás label_feeds_access_key: Chave de acesso ao Atom - notice_api_access_key_reseted: Sua chave de acesso a API foi redefinida. - setting_rest_api_enabled: Habilitar a api REST - label_missing_api_access_key: Chave de acesso a API faltando + notice_api_access_key_reseted: Sua chave de acesso à API foi redefinida. + setting_rest_api_enabled: Habilitar a API REST + label_missing_api_access_key: Chave de acesso à API faltando label_missing_feeds_access_key: Chave de acesso ao Atom faltando text_line_separated: Múltiplos valores permitidos (uma linha para cada valor). setting_mail_handler_body_delimiters: Truncar e-mails após uma destas linhas @@ -918,7 +917,6 @@ pt-BR: error_can_not_remove_role: Este papel está em uso e não pode ser excluído. error_can_not_delete_tracker: Este tipo de tarefa está atribuído a alguma(s) tarefa(s) e não pode ser excluído. field_principal: Principal - label_my_page_block: Meu bloco de página notice_failed_to_save_members: "Falha ao salvar membro(s): %{errors}." text_zoom_out: Afastar zoom text_zoom_in: Aproximar zoom @@ -929,10 +927,8 @@ pt-BR: project_module_calendar: Calendário button_edit_associated_wikipage: "Editar página wiki relacionada: %{page_title}" field_text: Campo de texto - label_user_mail_option_only_owner: Somente para as coisas que eu criei setting_default_notification_option: Opção padrão de notificação - label_user_mail_option_only_my_events: Somente para as coisas que eu esteja observando ou esteja envolvido - label_user_mail_option_only_assigned: Somente para as coisas que estejam atribuídas a mim + label_user_mail_option_only_my_events: Somente de tarefas que observo ou que esteja envolvido label_user_mail_option_none: Sem eventos field_member_of_group: Responsável pelo grupo field_assigned_to_role: Papel do responsável @@ -991,16 +987,12 @@ pt-BR: description_project_scope: Escopo da pesquisa description_filter: Filtro description_user_mail_notification: Configuração de notificações por e-mail - description_date_from: Digite a data inicial description_message_content: Conteúdo da mensagem description_available_columns: Colunas disponíveis - description_date_range_interval: Escolha um período selecionando a data de início e fim description_issue_category_reassign: Escolha uma categoria de tarefas description_search: Campo de busca description_notes: Notas - description_date_range_list: Escolha um período a partir da lista description_choose_project: Projetos - description_date_to: Digite a data final description_query_sort_criteria_attribute: Atributo de ordenação description_wiki_subpages_reassign: Escolha uma nova página pai description_selected_columns: Colunas selecionadas @@ -1034,7 +1026,7 @@ pt-BR: text_issue_conflict_resolution_cancel: Descartar todas as minhas mudanças e reexibir %{link} permission_manage_related_issues: Gerenciar tarefas relacionadas field_auth_source_ldap_filter: Filtro LDAP - label_search_for_watchers: Procurar por outros observadores para adiconar + label_search_for_watchers: Procurar por outros observadores para adicionar notice_account_deleted: Sua conta foi excluída permanentemente. setting_unsubscribe: Permitir aos usuários excluir sua própria conta button_delete_my_account: Excluir minha conta @@ -1043,8 +1035,8 @@ pt-BR: Sua conta será excluída permanentemente, sem qualquer forma de reativá-la. error_session_expired: A sua sessão expirou. Por favor, faça login novamente. text_session_expiration_settings: "Aviso: a alteração dessas configurações pode expirar as sessões atuais, incluindo a sua." - setting_session_lifetime: duração máxima da sessão - setting_session_timeout: tempo limite de inatividade da sessão + setting_session_lifetime: Duração máxima da sessão + setting_session_timeout: Tempo limite de inatividade da sessão label_session_expiration: "Expiração da sessão" permission_close_project: Fechar / reabrir o projeto label_show_closed_projects: Visualizar projetos fechados @@ -1133,13 +1125,13 @@ pt-BR: label_group_non_member: Usuários não membros label_add_projects: Adicionar projetos 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 label_users_visibility_all: Todos usuários ativos label_users_visibility_members_of_visible_projects: Membros de projetos visíveis label_edit_attachments: Editar arquivos anexados - setting_link_copied_issue: Linkar tarefas copiadas - label_link_copied_issue: Linkar tarefas copiadas + setting_link_copied_issue: Relacionar tarefas copiadas + label_link_copied_issue: Relacionar tarefas copiadas label_ask: Perguntar label_search_attachments_yes: Procurar nome do arquivo e descrição anexados label_search_attachments_no: Não procurar anexados @@ -1170,7 +1162,7 @@ pt-BR: notice_import_finished: "%{count} itens foram importados" notice_import_finished_with_errors: "%{count} fora de %{total} não puderam ser importados" error_invalid_file_encoding: O arquivo não é válido %{encoding} é a codificação do arquivo - error_invalid_csv_file_or_settings: O arquivo não é um arquivo CSV ou não corresponde as + error_invalid_csv_file_or_settings: O arquivo não é um arquivo CSV ou não corresponde às definições abaixo error_can_not_read_import_file: Ocorreu um erro ao ler o arquivo para importação permission_import_issues: Importar tarefas @@ -1184,7 +1176,7 @@ pt-BR: label_quote_char: Citar label_double_quote_char: Citação dupla label_fields_mapping: Mapeamento de campos - label_file_content_preview: Pré-visualir conteúdo do arquivo + label_file_content_preview: Pré-visualizar conteúdo do arquivo label_create_missing_values: Criar valores em falta button_import: Importar field_total_estimated_hours: Tempo estimado geral @@ -1199,7 +1191,7 @@ pt-BR: setting_attachment_extensions_denied: Negar extensões label_any_open_issues: Quaisquer tarefas abertas label_no_open_issues: Sem tarefas abertas - label_default_values_for_new_users: Valor padrão para novos usuários + label_default_values_for_new_users: Valores padrões para novos usuários setting_sys_api_key: Chave de API setting_lost_password: Perdi minha senha mail_subject_security_notification: Notificação de segurança @@ -1225,4 +1217,28 @@ pt-BR: setting_new_item_menu_tab: Aba menu do projeto para criação de novos objetos label_new_object_tab_enabled: Exibir o "+" suspenso error_no_projects_with_tracker_allowed_for_new_issue: Não há projetos com tipos de tarefa para os quais você pode criar uma tarefa + field_textarea_font: Fonte usada para áreas de texto + label_font_default: Fonte padrão + label_font_monospace: Fonte monoespaçada + label_font_proportional: Fonte proporcional + setting_timespan_format: Formato de tempo + label_table_of_contents: Índice + setting_commit_logs_formatting: Aplicar formatação de texto às mensagens de commit + setting_mail_handler_enable_regex_delimiters: Ativar a utilização de expressões regulares + error_move_of_child_not_possible: 'A subtarefa %{child} não pode ser movida para o novo projeto: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: O tempo gasto não pode ser alterado numa tarefa que será apagada + setting_timelog_required_fields: Campos obrigatórios para registro de horas + label_attribute_of_object: '%{object_name} %{name}' + label_user_mail_option_only_assigned: Somente de tarefas que observo ou que estão atribuídas a mim + label_user_mail_option_only_owner: Somente de tarefas que observo ou que foram criadas por mim + warning_fields_cleared_on_bulk_edit: As alterações vão apagar valores de um ou mais campos nos objetos selecionados + field_updated_by: Atualizado por + field_last_updated_by: Última atualização por + field_full_width_layout: Layout utiliza toda a largura + label_last_notes: Últimas notas + field_digest: Verificador + field_default_assigned_to: Responsável padrão + setting_show_custom_fields_on_registration: Mostrar campos personalizados no registro + permission_view_news: Ver notícias + label_no_preview_alternative_html: Visualização não disponível. Faça o %{link} do arquivo. + label_no_preview_download: download diff --git a/config/locales/pt.yml b/config/locales/pt.yml index 281c93c1c..1d7bda444 100644 --- a/config/locales/pt.yml +++ b/config/locales/pt.yml @@ -139,8 +139,9 @@ pt: circular_dependency: "Esta relação iria criar uma dependência circular" cant_link_an_issue_with_a_descendant: "Não é possível ligar uma tarefa a uma sub-tarefa que lhe é pertencente" earlier_than_minimum_start_date: "não pode ser antes de %{date} devido a tarefas precedentes" + not_a_regexp: "não é uma expressão regular válida" + open_issue_with_closed_parent: "Uma tarefa aberta não pode ser relacionada com uma tarefa pai fechada" - actionview_instancetag_blank_option: Selecione general_text_No: 'Não' @@ -195,8 +196,7 @@ pt: mail_subject_account_activation_request: "Pedido de ativação da conta %{value}" mail_body_account_activation_request: "Um novo utilizador (%{value}) registou-se. A sua conta está à espera de aprovação:" mail_subject_reminder: "Tem %{count} tarefa(s) para entregar nos próximos %{days} dias" - mail_body_reminder: "%{count} tarefa(s) que estão atribuídas a si estão agendadas para estarem completas nos próximos %{days} dias:" - + mail_body_reminder: "%{count} tarefa(s) que estão atribuídas a si estão agendadas para serem terminadas nos próximos %{days} dias:" field_name: Nome field_description: Descrição @@ -465,7 +465,6 @@ pt: label_internal: Interno label_last_changes: "últimas %{count} alterações" label_change_view_all: Ver todas as alterações - label_personalize_page: Personalizar esta página label_comment: Comentário label_comment_plural: Comentários label_x_comments: @@ -559,13 +558,13 @@ pt: label_loading: A carregar... label_relation_new: Nova relação label_relation_delete: Apagar relação - label_relates_to: relacionado a - label_duplicates: duplica - label_duplicated_by: duplicado por - label_blocks: bloqueia - label_blocked_by: bloqueado por - label_precedes: precede - label_follows: segue + label_relates_to: Relacionado a + label_duplicates: Duplica + label_duplicated_by: Duplicado por + label_blocks: Bloqueia + label_blocked_by: Bloqueada por + label_precedes: Precede + label_follows: Segue label_stay_logged_in: Guardar sessão label_disabled: desativado label_show_completed_versions: Mostrar versões acabadas @@ -581,13 +580,13 @@ pt: label_reply_plural: Respostas label_send_information: Enviar dados da conta para o utilizador label_year: Ano - label_month: mês + label_month: Mês label_week: Semana label_date_from: De - label_date_to: Para + label_date_to: A label_language_based: Baseado na língua do utilizador label_sort_by: "Ordenar por %{value}" - label_send_test_email: enviar um e-mail de teste + label_send_test_email: Enviar um e-mail de teste label_feeds_access_key_created_on: "Chave Atom criada há %{value} atrás" label_module_plural: Módulos label_added_time_by: "Adicionado por %{author} há %{age} atrás" @@ -601,8 +600,8 @@ pt: label_theme: Tema label_default: Padrão label_search_titles_only: Procurar apenas em títulos - label_user_mail_option_all: "Para qualquer evento em todos os meus projetos" - label_user_mail_option_selected: "Para qualquer evento apenas nos projetos selecionados..." + label_user_mail_option_all: "Qualquer evento em todos os meus projetos" + label_user_mail_option_selected: "Qualquer evento mas apenas nos projetos selecionados" label_user_mail_no_self_notified: "Não quero ser notificado de alterações feitas por mim" label_registration_activation_by_email: Ativação da conta por e-mail label_registration_manual_activation: Ativação manual da conta @@ -611,7 +610,6 @@ pt: label_age: Idade label_change_properties: Mudar propriedades label_general: Geral - label_more: Mais label_scm: SCM label_plugins: Extensões label_ldap_authentication: Autenticação LDAP @@ -621,7 +619,6 @@ pt: label_preferences: Preferências label_chronological_order: Em ordem cronológica label_reverse_chronological_order: Em ordem cronológica inversa - label_planning: Planeamento label_incoming_emails: E-mails recebidos label_generate_key: Gerar uma chave label_issue_watchers: Observadores @@ -676,9 +673,9 @@ pt: text_subprojects_destroy_warning: "O(s) seu(s) sub-projeto(s): %{value} também será/serão apagado(s)." text_workflow_edit: Selecione uma função e um tipo de tarefa para editar o fluxo de trabalho text_are_you_sure: Tem a certeza? - text_tip_issue_begin_day: tarefa a começar neste dia - text_tip_issue_end_day: tarefa a acabar neste dia - text_tip_issue_begin_end_day: tarefa a começar e acabar neste dia + text_tip_issue_begin_day: tarefa inicia neste dia + text_tip_issue_end_day: tarefa termina neste dia + text_tip_issue_begin_end_day: tarefa a iniciar e terminar neste dia text_caracters_maximum: "máximo %{count} caráteres." text_caracters_minimum: "Deve ter pelo menos %{count} caráteres." text_length_between: "Deve ter entre %{min} e %{max} caráteres." @@ -708,7 +705,7 @@ pt: text_user_wrote: "%{value} escreveu:" text_enumeration_destroy_question: "%{count} objetos estão atribuídos a este valor." text_enumeration_category_reassign_to: 'Re-atribuí-los para este valor:' - text_email_delivery_not_configured: "Entrega por e-mail não está configurada, e as notificação estão desativadas.\nConfigure o seu servidor de SMTP em config/configuration.yml e reinicie a aplicação para ativar estas funcionalidades." + text_email_delivery_not_configured: "O envio de e-mail não está configurado, e as notificação estão desativadas.\nConfigure o seu servidor de SMTP em config/configuration.yml e reinicie a aplicação para ativar estas funcionalidades." default_role_manager: Gestor default_role_developer: Programador @@ -753,7 +750,7 @@ pt: permission_select_project_modules: Selecionar módulos do projeto permission_edit_wiki_pages: Editar páginas da wiki permission_add_issue_watchers: Adicionar observadores - permission_view_gantt: ver diagrama de Gantt + permission_view_gantt: Ver diagrama de Gantt permission_move_issues: Mover tarefas permission_manage_issue_relations: Gerir relações de tarefas permission_delete_wiki_pages: Apagar páginas de wiki @@ -780,7 +777,7 @@ pt: permission_view_wiki_pages: Ver wiki permission_rename_wiki_pages: Renomear páginas de wiki permission_edit_time_entries: Editar entradas de tempo - permission_edit_own_issue_notes: Editar as prórpias notas + permission_edit_own_issue_notes: Editar as próprias notas setting_gravatar_enabled: Utilizar ícones Gravatar label_example: Exemplo text_repository_usernames_mapping: "Selecionar ou atualizar o utilizador de Redmine mapeado a cada nome de utilizador encontrado no repositório.\nUtilizadores com o mesmo nome de utilizador ou e-mail no Redmine e no repositório são mapeados automaticamente." @@ -869,7 +866,7 @@ pt: label_update_issue_done_ratios: Atualizar percentagens de progresso da tarefa setting_start_of_week: Iniciar calendários a permission_view_issues: Ver tarefas - label_display_used_statuses_only: Só exibir estados empregues por este tipo de tarefa + label_display_used_statuses_only: Exibir apenas estados utilizados por este tipo de tarefa label_revision_id: Revisão %{value} label_api_access_key: Chave de acesso API label_api_access_key_created_on: Chave de acesso API criada há %{value} @@ -903,7 +900,6 @@ pt: error_can_not_remove_role: Esta função está atualmente em uso e não pode ser apagada. error_can_not_delete_tracker: Existem ainda tarefas nesta categoria. Não é possível apagar este tipo de tarefa. field_principal: Principal - label_my_page_block: Bloco da minha página notice_failed_to_save_members: "Erro ao guardar o(s) membro(s): %{errors}." text_zoom_out: Reduzir text_zoom_in: Ampliar @@ -914,10 +910,8 @@ pt: project_module_calendar: Calendário button_edit_associated_wikipage: "Editar página Wiki associada: %{page_title}" field_text: Campo de texto - label_user_mail_option_only_owner: Apenas para tarefas das quais sou proprietário setting_default_notification_option: Opção predefinida de notificação label_user_mail_option_only_my_events: Apenas para tarefas que observo ou em que estou envolvido - label_user_mail_option_only_assigned: Apenas para tarefas que me foram atribuídas label_user_mail_option_none: Sem eventos field_member_of_group: Grupo do titular field_assigned_to_role: Função do titular @@ -977,16 +971,12 @@ pt: description_project_scope: Âmbito da pesquisa description_filter: Filtro description_user_mail_notification: Configurações das notificações por e-mail - description_date_from: Introduza data de início description_message_content: Conteúdo da mensagem description_available_columns: Colunas disponíveis - description_date_range_interval: Escolha o intervalo selecionando a data de início e de fim description_issue_category_reassign: Escolha a categoria da tarefa description_search: Campo de pesquisa description_notes: Notas - description_date_range_list: Escolha o intervalo da lista description_choose_project: Projeto - description_date_to: Introduza data de fim description_query_sort_criteria_attribute: Ordenar atributos description_wiki_subpages_reassign: Escolha nova página pai description_selected_columns: Colunas selecionadas @@ -1055,8 +1045,8 @@ pt: label_attribute_of_assigned_to: "%{name} do utilizador titular" label_attribute_of_fixed_version: "%{name} da Versão" label_copy_subtasks: Copiar sub-tarefas - label_copied_to: copiado para - label_copied_from: copiado de + label_copied_to: Copiado para + label_copied_from: Copiado de label_any_issues_in_project: tarefas do projeto label_any_issues_not_in_project: tarefas sem projeto field_private_notes: Notas privadas @@ -1091,8 +1081,7 @@ pt: notice_account_not_activated_yet: Ainda não ativou a sua conta. Se desejar receber um novo e-mail de ativação, por favor clique nesta ligação. notice_account_locked: A sua conta está bloqueada. - notice_account_register_done: A conta foi criada com sucesso! Um e-mail contendo - as instruções para ativar a sua conta foi enviado para %{email}. + notice_account_register_done: A conta foi criada com sucesso! Um e-mail contendo as instruções para ativar a sua conta foi enviado para %{email}. label_hidden: Escondido label_visibility_private: apenas para mim label_visibility_roles: apenas para estas funções @@ -1108,8 +1097,7 @@ pt: label_link_values_to: Ligação de valores ao URL setting_force_default_language_for_anonymous: Forçar língua por omissão para utilizadores anónimos setting_force_default_language_for_loggedin: Forçar língua por omissão para utilizadores registados - label_custom_field_select_type: Selecione o tipo de objeto ao qual o campo personalizado - será atribuído + label_custom_field_select_type: Selecione o tipo de objeto ao qual o campo personalizado será atribuído label_issue_assigned_to_updated: Utilizador titular atualizado label_check_for_updates: Verificar atualizações label_latest_compatible_version: Última versão compatível @@ -1207,4 +1195,29 @@ pt: setting_new_item_menu_tab: Aba de Projeto para criar novos objetos label_new_object_tab_enabled: Apresentar a aba "+" error_no_projects_with_tracker_allowed_for_new_issue: Não há projetos com tipos de tarefa para os quais você pode criar uma tarefa + field_textarea_font: Fonte para áreas de texto + label_font_default: Default font + label_font_monospace: Fonte Monospaced + label_font_proportional: Fonte proporcional + setting_timespan_format: Formato de Data/Hora + label_table_of_contents: Índice + setting_commit_logs_formatting: Aplicar formatação de texto às mensagens de commit + setting_mail_handler_enable_regex_delimiters: Ativar a utilização de expressões regulares + error_move_of_child_not_possible: 'A sub-tarefa %{child} não pode ser movida para o novo + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: O tempo gasto não pode ser alterado numa tarefa que será apagada + setting_timelog_required_fields: Campos obrigatórios para o registo de tempo + label_attribute_of_object: '%{object_name} %{name}' + label_user_mail_option_only_assigned: Apenas para tarefas que observo ou que me estão atribuídas + label_user_mail_option_only_owner: Apenas para tarefas que observo ou que eu criei + warning_fields_cleared_on_bulk_edit: As alterações vão apagar valores de um ou mais campos nos objetos selecionados + field_updated_by: Atualizado por + field_last_updated_by: Última atualização por + field_full_width_layout: Layout utiliza toda a largura + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/ro.yml b/config/locales/ro.yml index 3e0de0374..f364e46a4 100644 --- a/config/locales/ro.yml +++ b/config/locales/ro.yml @@ -125,6 +125,8 @@ ro: circular_dependency: "Această relație ar crea o dependență circulară" cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" actionview_instancetag_blank_option: Selectați @@ -512,7 +514,6 @@ ro: label_internal: Intern label_last_changes: "ultimele %{count} schimbări" label_change_view_all: Afișează toate schimbările - label_personalize_page: Personalizează aceasta pagina label_comment: Comentariu label_comment_plural: Comentarii label_x_comments: @@ -659,7 +660,6 @@ ro: label_age: vechime label_change_properties: Schimbă proprietățile label_general: General - label_more: Mai mult label_scm: SCM label_plugins: Plugin-uri label_ldap_authentication: autentificare LDAP @@ -669,7 +669,6 @@ ro: label_preferences: Preferințe label_chronological_order: în ordine cronologică label_reverse_chronological_order: În ordine invers cronologică - label_planning: Planificare label_incoming_emails: Mesaje primite label_generate_key: Generează o cheie label_issue_watchers: Cine urmărește @@ -891,7 +890,6 @@ ro: error_can_not_remove_role: This role is in use and can not be deleted. error_can_not_delete_tracker: This tracker contains issues and cannot be deleted. field_principal: Principal - label_my_page_block: My page block notice_failed_to_save_members: "Failed to save member(s): %{errors}." text_zoom_out: Zoom out text_zoom_in: Zoom in @@ -902,10 +900,8 @@ ro: project_module_calendar: Calendar button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}" field_text: Text field - label_user_mail_option_only_owner: Only for things I am the owner of setting_default_notification_option: Default notification option label_user_mail_option_only_my_events: Only for things I watch or I'm involved in - label_user_mail_option_only_assigned: Only for things I am assigned to label_user_mail_option_none: No events field_member_of_group: Assignee's group field_assigned_to_role: Assignee's role @@ -962,16 +958,12 @@ ro: description_project_scope: Search scope description_filter: Filter description_user_mail_notification: Mail notification settings - description_date_from: Enter start date description_message_content: Message content description_available_columns: Available Columns - description_date_range_interval: Choose range by selecting start and end date description_issue_category_reassign: Choose issue category description_search: Searchfield description_notes: Notes - description_date_range_list: Choose range from list description_choose_project: Projects - description_date_to: Enter end date description_query_sort_criteria_attribute: Sort attribute description_wiki_subpages_reassign: Choose new parent page description_selected_columns: Selected Columns @@ -1203,5 +1195,31 @@ ro: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 80ab11e3f..630d9c987 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -207,6 +207,8 @@ ru: circular_dependency: "Такая связь приведет к циклической зависимости" cant_link_an_issue_with_a_descendant: "Задача не может быть связана со своей подзадачей" earlier_than_minimum_start_date: "не может быть раньше %{date} из-за предыдущих задач" + not_a_regexp: "не является допустимым регулярным выражением" + open_issue_with_closed_parent: "Открытая задача не может быть добавлена к закрытой родительской задаче" support: array: @@ -322,10 +324,10 @@ ru: field_description: Описание field_done_ratio: Готовность field_downloads: Загрузки - field_due_date: Дата завершения + field_due_date: Срок завершения field_editable: Редактируемое field_effective_date: Дата - field_estimated_hours: Оценка трудозатрат + field_estimated_hours: Оценка временных затрат field_field_format: Формат field_filename: Файл field_filesize: Размер @@ -573,10 +575,8 @@ ru: label_months_from: месяцев(ца) с label_month: Месяц label_more_than_ago: более, чем дней(я) назад - label_more: Больше label_my_account: Моя учётная запись label_my_page: Моя страница - label_my_page_block: Блок моей страницы label_my_projects: Мои проекты label_new: Новый label_new_statuses_allowed: Разрешенные новые статусы @@ -604,8 +604,6 @@ ru: label_password_lost: Восстановление пароля label_permissions_report: Отчёт по правам доступа label_permissions: Права доступа - label_personalize_page: Персонализировать данную страницу - label_planning: Планирование label_please_login: Пожалуйста, войдите. label_plugins: Модули label_precedes: следующая @@ -634,7 +632,7 @@ ru: label_related_issues: Связанные задачи label_relates_to: связана с label_relation_delete: Удалить связь - label_relation_new: Новое отношение + label_relation_new: Новая связь label_renamed: переименовано label_reply_plural: Ответы label_report: Отчёт @@ -694,9 +692,7 @@ ru: label_user_mail_no_self_notified: "Не извещать об изменениях, которые я сделал сам" label_user_mail_option_all: "О всех событиях во всех моих проектах" label_user_mail_option_selected: "О всех событиях только в выбранном проекте..." - label_user_mail_option_only_owner: Только для объектов, для которых я являюсь владельцем label_user_mail_option_only_my_events: Только для объектов, которые я отслеживаю или в которых участвую - label_user_mail_option_only_assigned: Только для объектов, которые назначены мне label_user_new: Новый пользователь label_user_plural: Пользователи label_version: Версия @@ -1076,16 +1072,12 @@ ru: description_project_scope: Область поиска description_filter: Фильтр description_user_mail_notification: Настройки почтовых оповещений - description_date_from: Введите дату начала description_message_content: Содержание сообщения description_available_columns: Доступные столбцы - description_date_range_interval: Выберите диапазон, установив дату начала и дату окончания description_issue_category_reassign: Выберите категорию задачи description_search: Поле поиска description_notes: Примечания - description_date_range_list: Выберите диапазон из списка description_choose_project: Проекты - description_date_to: Введите дату выполнения description_query_sort_criteria_attribute: Критерий сортировки description_wiki_subpages_reassign: Выбрать новую родительскую страницу description_selected_columns: Выбранные столбцы @@ -1270,7 +1262,7 @@ ru: label_file_content_preview: Предпросмотр содержимого файла label_create_missing_values: Создать недостающие значения button_import: Импорт - field_total_estimated_hours: Всего осталось времени + field_total_estimated_hours: Общая оценка временных затрат label_api: API label_total_plural: Итоги label_assigned_issues: Назначенные задачи @@ -1309,4 +1301,29 @@ ru: setting_new_item_menu_tab: Вкладка меню проекта для создания новых объектов label_new_object_tab_enabled: Отображать выпадающий список "+" error_no_projects_with_tracker_allowed_for_new_issue: Отсутствуют проекты, по трекерам которых вы можете создавать задачи + field_textarea_font: Шрифт для текстовых полей + label_font_default: Шрифт по умолчанию + label_font_monospace: Моноширинный шрифт + label_font_proportional: Пропорциональный шрифт + setting_timespan_format: Формат промежутка времени + label_table_of_contents: Содержание + setting_commit_logs_formatting: Использовать форматирование текста для комментариев хранилища + setting_mail_handler_enable_regex_delimiters: Использовать регулярные выражения + error_move_of_child_not_possible: 'Подзадача %{child} не может быть перемещена в новый + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Затраченное время не может быть переназначено на задачу, которая будет удалена + setting_timelog_required_fields: Обязательные поля для трудозатрат + label_attribute_of_object: '%{name} объекта %{object_name}' + label_user_mail_option_only_assigned: Только для объектов, которые я отслеживаю или которые мне назначены + label_user_mail_option_only_owner: Только для объектов, которые я отслеживаю или для которых я владелец + warning_fields_cleared_on_bulk_edit: Изменения приведут к удалению значений одного или нескольких полей выбранных объектов + field_updated_by: Кем изменено + field_last_updated_by: Последний изменивший + field_full_width_layout: Растягивать по ширине страницы + label_last_notes: Последние примечания + field_digest: Контрольная сумма + field_default_assigned_to: Назначать по умолчанию + setting_show_custom_fields_on_registration: Показывать настраиваемые поля при регистрации + permission_view_news: Просмотр новостей + label_no_preview_alternative_html: Предпросмотр недоступен. %{link} файл. + label_no_preview_download: Скачать diff --git a/config/locales/sk.yml b/config/locales/sk.yml index fb5c32552..bd3b9779c 100644 --- a/config/locales/sk.yml +++ b/config/locales/sk.yml @@ -129,6 +129,8 @@ sk: circular_dependency: "Tento vzťah by vytvoril cyklickú závislosť" cant_link_an_issue_with_a_descendant: "Nemožno prepojiť úlohu s niektorou z podúloh" earlier_than_minimum_start_date: "nemôže byť skorší ako %{date} z dôvodu nadväznosti na predchádzajúce úlohy" + 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" actionview_instancetag_blank_option: Vyberte @@ -445,7 +447,6 @@ sk: label_internal: Interný label_last_changes: "posledných %{count} zmien" label_change_view_all: Zobraziť všetky zmeny - label_personalize_page: Prispôsobiť túto stránku label_comment: Komentár label_comment_plural: Komentáre label_x_comments: @@ -588,7 +589,6 @@ sk: label_age: Vek label_change_properties: Zmeniť vlastnosti label_general: Všeobecné - label_more: Viac label_scm: SCM label_plugins: Pluginy label_ldap_authentication: Autentifikácia LDAP @@ -703,7 +703,6 @@ sk: enumeration_doc_categories: Kategórie dokumentov enumeration_activities: Aktivity (sledovanie času) error_scm_annotate: "Položka neexistuje, alebo nemôže byť komentovaná." - label_planning: Plánovanie text_subprojects_destroy_warning: "Jeho podprojekty: %{value} budú tiež vymazané." label_and_its_subprojects: "%{value} a jeho podprojekty" mail_body_reminder: "Nasledovné úlohy (%{count}), ktoré sú vám priradené, majú byť hotové za %{days} dní:" @@ -891,7 +890,6 @@ sk: error_can_not_remove_role: "Táto rola sa používa a nemôže byť vymazaná." error_can_not_delete_tracker: "Tento front obsahuje úlohy a nemôže byť vymazaný." field_principal: Objednávateľ - label_my_page_block: Blok obsahu notice_failed_to_save_members: "Nepodarilo sa uložiť členov: %{errors}." text_zoom_out: Oddialiť text_zoom_in: Priblížiť @@ -902,10 +900,8 @@ sk: project_module_calendar: Kalendár button_edit_associated_wikipage: "Upraviť súvisiacu wikistránku: %{page_title}" field_text: Textové pole - label_user_mail_option_only_owner: "Len pre veci, ktorých som vlastníkom" setting_default_notification_option: Predvolené nastavenie upozornení label_user_mail_option_only_my_events: Len pre veci, ktoré sledujem, alebo v ktorých som zapojený - label_user_mail_option_only_assigned: "Len pre veci, ktoré mi boli priradené" label_user_mail_option_none: "Žiadne udalosti" field_member_of_group: "Skupina priradeného používateľa" field_assigned_to_role: "Rola priradeného používateľa" @@ -962,16 +958,12 @@ sk: description_project_scope: Rozsah vyhľadávania description_filter: Filter description_user_mail_notification: Nastavenia upozornení emailom - description_date_from: Zadajte počiatočný dátum description_message_content: Obsah správy description_available_columns: Dostupné stĺpce - description_date_range_interval: Zvoľte obdobie zadaním počiatočného a koncového dátumu description_issue_category_reassign: Vybrať kategóriu úlohy description_search: Vyhľadávanie description_notes: Poznámky - description_date_range_list: Vyberte zo zoznamu obdobie description_choose_project: Projekty - description_date_to: Zadajte koncový dátum description_query_sort_criteria_attribute: Atribút triedenia description_wiki_subpages_reassign: Vybrať novú nadradenú stránku description_selected_columns: Vybrané stĺpce @@ -1198,5 +1190,31 @@ sk: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/sl.yml b/config/locales/sl.yml index b4b217a1c..672dd593d 100644 --- a/config/locales/sl.yml +++ b/config/locales/sl.yml @@ -129,6 +129,8 @@ sl: circular_dependency: "Ta odnos bi povzročil krožno odvisnost" cant_link_an_issue_with_a_descendant: "Zahtevek ne more biti povezan s svojo podnalogo" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" actionview_instancetag_blank_option: Prosimo izberite @@ -506,7 +508,6 @@ sl: label_internal: Notranji label_last_changes: "zadnjih %{count} sprememb" label_change_view_all: Poglej vse spremembe - label_personalize_page: Individualiziraj to stran label_comment: Komentar label_comment_plural: Komentarji label_x_comments: @@ -653,7 +654,6 @@ sl: label_age: Starost label_change_properties: Sprememba lastnosti label_general: Splošno - label_more: Več label_scm: SCM label_plugins: Vtičniki label_ldap_authentication: LDAP overovljanje @@ -663,7 +663,6 @@ sl: label_preferences: Preference label_chronological_order: Kronološko label_reverse_chronological_order: Obrnjeno kronološko - label_planning: Načrtovanje label_incoming_emails: Prihajajoča e-pošta label_generate_key: Ustvari ključ label_issue_watchers: Spremljevalci @@ -893,7 +892,6 @@ sl: error_can_not_remove_role: Ta vloga je v uporabi in je ni mogoče izbrisati. error_can_not_delete_tracker: Ta sledilnik vsebuje zahtevke in se ga ne more izbrisati. field_principal: Upravnik varnosti - label_my_page_block: Moj gradnik strani notice_failed_to_save_members: "Shranjevanje uporabnika(ov) ni uspelo: %{errors}." text_zoom_out: Približaj text_zoom_in: Oddalji @@ -904,10 +902,8 @@ sl: project_module_calendar: Koledear button_edit_associated_wikipage: "Uredi povezano Wiki stran: %{page_title}" field_text: Besedilno polje - label_user_mail_option_only_owner: Samo za stvari katerih lastnik sem setting_default_notification_option: Privzeta možnost obveščanja label_user_mail_option_only_my_events: Samo za stvari, ki jih opazujem ali sem v njih vpleten - label_user_mail_option_only_assigned: Samo za stvari, ki smo mi dodeljene label_user_mail_option_none: Noben dogodek field_member_of_group: Pooblaščenčeva skupina field_assigned_to_role: Pooblaščenčeva vloga @@ -970,16 +966,12 @@ sl: description_project_scope: Search scope description_filter: Filter description_user_mail_notification: Mail notification settings - description_date_from: Enter start date description_message_content: Message content description_available_columns: Available Columns - description_date_range_interval: Choose range by selecting start and end date description_issue_category_reassign: Choose issue category description_search: Searchfield description_notes: Notes - description_date_range_list: Choose range from list description_choose_project: Projects - description_date_to: Enter end date description_query_sort_criteria_attribute: Sort attribute description_wiki_subpages_reassign: Choose new parent page description_selected_columns: Selected Columns @@ -1208,5 +1200,31 @@ sl: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/sq.yml b/config/locales/sq.yml index cc38be276..e0ea62eda 100644 --- a/config/locales/sq.yml +++ b/config/locales/sq.yml @@ -130,6 +130,8 @@ sq: circular_dependency: "Ky relacion do te krijoje nje varesi ciklike (circular dependency)" cant_link_an_issue_with_a_descendant: "Nje ceshtje nuk mund te lidhet me nenceshtje" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" actionview_instancetag_blank_option: Zgjidhni @@ -517,7 +519,6 @@ sq: label_my_page: Faqja ime label_my_account: Llogaria ime label_my_projects: Projektet e mia - label_my_page_block: My page block label_administration: Administrim label_login: Login label_logout: Dalje @@ -610,7 +611,6 @@ sq: label_internal: I brendshem label_last_changes: "%{count} ndryshimet e fundit" label_change_view_all: Shih gjithe ndryshimet - label_personalize_page: Personalizo kete Faqe label_comment: Koment label_comment_plural: Komente label_x_comments: @@ -767,8 +767,6 @@ sq: label_user_mail_option_selected: "For any event on the selected projects only..." label_user_mail_option_none: "No events" label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in" - label_user_mail_option_only_assigned: "Only for things I am assigned to" - label_user_mail_option_only_owner: "Only for things I am the owner of" label_user_mail_no_self_notified: "I don't want to be notified of changes that I make myself" label_registration_activation_by_email: account activation by email label_registration_manual_activation: manual account activation @@ -777,7 +775,6 @@ sq: label_age: Age label_change_properties: Change properties label_general: General - label_more: More label_scm: SCM label_plugins: Plugins label_ldap_authentication: LDAP authentication @@ -787,7 +784,6 @@ sq: label_preferences: Preferences label_chronological_order: In chronological order label_reverse_chronological_order: In reverse chronological order - label_planning: Planning label_incoming_emails: Incoming emails label_generate_key: Generate a key label_issue_watchers: Watchers @@ -1007,10 +1003,6 @@ sq: description_all_columns: All Columns description_issue_category_reassign: Choose issue category description_wiki_subpages_reassign: Choose new parent page - description_date_range_list: Choose range from list - description_date_range_interval: Choose range by selecting start and end date - description_date_from: Enter start date - description_date_to: Enter end date error_session_expired: Your session has expired. Please login again. text_session_expiration_settings: "Warning: changing these settings may expire the current sessions including yours." setting_session_lifetime: Session maximum lifetime @@ -1204,5 +1196,31 @@ sq: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/sr-YU.yml b/config/locales/sr-YU.yml index 6ef7aba69..550886b9f 100644 --- a/config/locales/sr-YU.yml +++ b/config/locales/sr-YU.yml @@ -133,6 +133,8 @@ sr-YU: circular_dependency: "Ova veza će stvoriti kružnu referencu" cant_link_an_issue_with_a_descendant: "Problem ne može biti povezan sa jednim od svojih podzadataka" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" actionview_instancetag_blank_option: Molim odaberite @@ -477,7 +479,6 @@ sr-YU: label_my_page: Moja stranica label_my_account: Moj nalog label_my_projects: Moji projekti - label_my_page_block: My page block label_administration: Administracija label_login: Prijava label_logout: Odjava @@ -565,7 +566,6 @@ sr-YU: label_internal: Unutrašnji label_last_changes: "poslednjih %{count} promena" label_change_view_all: Prikaži sve promene - label_personalize_page: Personalizuj ovu stranu label_comment: Komentar label_comment_plural: Komentari label_x_comments: @@ -723,7 +723,6 @@ sr-YU: label_age: Starost label_change_properties: Promeni svojstva label_general: Opšti - label_more: Više label_scm: SCM label_plugins: Dodatne komponente label_ldap_authentication: LDAP potvrda identiteta @@ -733,7 +732,6 @@ sr-YU: label_preferences: Podešavanja label_chronological_order: po hronološkom redosledu label_reverse_chronological_order: po obrnutom hronološkom redosledu - label_planning: Planiranje label_incoming_emails: Dolazne e-poruke label_generate_key: Generisanje ključa label_issue_watchers: Posmatrači @@ -909,10 +907,8 @@ sr-YU: project_module_calendar: Kalendar button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}" field_text: Text field - label_user_mail_option_only_owner: Samo za stvari koje posedujem setting_default_notification_option: Podrazumevana opcija za notifikaciju label_user_mail_option_only_my_events: Za dogadjaje koje pratim ili sam u njih uključen - label_user_mail_option_only_assigned: Za dogadjaje koji su mi dodeljeni lično label_user_mail_option_none: Bez obaveštenja field_member_of_group: Assignee's group field_assigned_to_role: Assignee's role @@ -970,16 +966,12 @@ sr-YU: description_project_scope: Search scope description_filter: Filter description_user_mail_notification: Mail notification settings - description_date_from: Enter start date description_message_content: Message content description_available_columns: Available Columns - description_date_range_interval: Choose range by selecting start and end date description_issue_category_reassign: Choose issue category description_search: Searchfield description_notes: Notes - description_date_range_list: Choose range from list description_choose_project: Projects - description_date_to: Enter end date description_query_sort_criteria_attribute: Sort attribute description_wiki_subpages_reassign: Choose new parent page description_selected_columns: Selected Columns @@ -1210,5 +1202,31 @@ sr-YU: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/sr.yml b/config/locales/sr.yml index 6c7100f96..293fb3f92 100644 --- a/config/locales/sr.yml +++ b/config/locales/sr.yml @@ -131,6 +131,8 @@ sr: circular_dependency: "Ова веза ће створити кружну референцу" cant_link_an_issue_with_a_descendant: "Проблем не може бити повезан са једним од својих подзадатака" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" actionview_instancetag_blank_option: Молим одаберите @@ -475,7 +477,6 @@ sr: label_my_page: Моја страница label_my_account: Мој налог label_my_projects: Моји пројекти - label_my_page_block: My page block label_administration: Администрација label_login: Пријава label_logout: Одјава @@ -563,7 +564,6 @@ sr: label_internal: Унутрашњи label_last_changes: "последњих %{count} промена" label_change_view_all: Прикажи све промене - label_personalize_page: Персонализуј ову страну label_comment: Коментар label_comment_plural: Коментари label_x_comments: @@ -721,7 +721,6 @@ sr: label_age: Старост label_change_properties: Промени својства label_general: Општи - label_more: Више label_scm: SCM label_plugins: Додатне компоненте label_ldap_authentication: LDAP потврда идентитета @@ -731,7 +730,6 @@ sr: label_preferences: Подешавања label_chronological_order: по хронолошком редоследу label_reverse_chronological_order: по обрнутом хронолошком редоследу - label_planning: Планирање label_incoming_emails: Долазне е-поруке label_generate_key: Генерисање кључа label_issue_watchers: Посматрачи @@ -908,10 +906,8 @@ sr: button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}" field_text: Text field - label_user_mail_option_only_owner: Only for things I am the owner of setting_default_notification_option: Default notification option label_user_mail_option_only_my_events: Only for things I watch or I'm involved in - label_user_mail_option_only_assigned: Only for things I am assigned to label_user_mail_option_none: No events field_member_of_group: Assignee's group field_assigned_to_role: Assignee's role @@ -968,16 +964,12 @@ sr: description_project_scope: Search scope description_filter: Filter description_user_mail_notification: Mail notification settings - description_date_from: Enter start date description_message_content: Message content description_available_columns: Available Columns - description_date_range_interval: Choose range by selecting start and end date description_issue_category_reassign: Choose issue category description_search: Searchfield description_notes: Notes - description_date_range_list: Choose range from list description_choose_project: Projects - description_date_to: Enter end date description_query_sort_criteria_attribute: Sort attribute description_wiki_subpages_reassign: Choose new parent page description_selected_columns: Selected Columns @@ -1209,5 +1201,31 @@ sr: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/sv.yml b/config/locales/sv.yml index 0aa205631..27abe1667 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -135,6 +135,8 @@ sv: 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" 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" + open_issue_with_closed_parent: "An open issue cannot be attached to a closed parent task" direction: ltr date: @@ -584,7 +586,6 @@ sv: label_my_page: Min sida label_my_account: Mitt konto label_my_projects: Mina projekt - label_my_page_block: '"Min sida"-block' label_administration: Administration label_login: Logga in label_logout: Logga ut @@ -679,7 +680,6 @@ sv: label_internal: Intern label_last_changes: "senaste %{count} ändringar" label_change_view_all: Visa alla ändringar - label_personalize_page: Anpassa denna sida label_comment: Kommentar label_comment_plural: Kommentarer label_x_comments: @@ -844,8 +844,6 @@ sv: label_user_mail_option_selected: "För alla händelser i markerade projekt..." label_user_mail_option_none: "Inga händelser" label_user_mail_option_only_my_events: "Endast för saker jag bevakar eller är inblandad i" - label_user_mail_option_only_assigned: "Endast för saker jag är tilldelad" - label_user_mail_option_only_owner: "Endast för saker jag äger" label_user_mail_no_self_notified: "Jag vill inte bli underrättad om ändringar som jag har gjort" label_registration_activation_by_email: kontoaktivering med mail label_registration_manual_activation: manuell kontoaktivering @@ -854,7 +852,6 @@ sv: label_age: Ålder label_change_properties: Ändra inställningar label_general: Allmänt - label_more: Mer label_scm: SCM label_plugins: Tillägg label_ldap_authentication: LDAP-autentisering @@ -864,7 +861,6 @@ sv: label_preferences: Användarinställningar label_chronological_order: I kronologisk ordning label_reverse_chronological_order: I omvänd kronologisk ordning - label_planning: Planering label_incoming_emails: Inkommande mail label_generate_key: Generera en nyckel label_issue_watchers: Bevakare @@ -1114,10 +1110,6 @@ sv: description_all_columns: Alla kolumner description_issue_category_reassign: Välj ärendekategori description_wiki_subpages_reassign: Välj ny föräldersida - description_date_range_list: Välj intervall från listan - description_date_range_interval: Ange intervall genom att välja start- och slutdatum - description_date_from: Ange startdatum - description_date_to: Ange slutdatum text_repository_identifier_info: 'Endast gemener (a-z), siffror, streck och understreck är tillåtna.
    När identifieraren sparats kan den inte ändras.' notice_account_not_activated_yet: Du har inte aktiverat ditt konto än. Om du vill få ett nytt aktiveringsbrev, klicka på denna länk . notice_account_locked: Ditt konto är låst. @@ -1241,5 +1233,31 @@ sv: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/th.yml b/config/locales/th.yml index 05574aa9e..f97998aa8 100644 --- a/config/locales/th.yml +++ b/config/locales/th.yml @@ -128,6 +128,8 @@ th: circular_dependency: "ความสัมพันธ์อ้างอิงเป็นวงกลม" cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" actionview_instancetag_blank_option: กรุณาเลือก @@ -446,7 +448,6 @@ th: label_internal: ภายใน label_last_changes: "last %{count} เปลี่ยนแปลง" label_change_view_all: ดูการเปลี่ยนแปลงทั้งหมด - label_personalize_page: ปรับแต่งหน้านี้ label_comment: ความเห็น label_comment_plural: ความเห็น label_x_comments: @@ -589,7 +590,6 @@ th: label_age: อายุ label_change_properties: เปลี่ยนคุณสมบัติ label_general: ทั่วๆ ไป - label_more: อื่น ๆ label_scm: ตัวจัดการต้นฉบับ label_plugins: ส่วนเสริม label_ldap_authentication: การยืนยันตัวตนโดยใช้ LDAP @@ -599,7 +599,6 @@ th: label_preferences: ค่าที่ชอบใจ label_chronological_order: เรียงจากเก่าไปใหม่ label_reverse_chronological_order: เรียงจากใหม่ไปเก่า - label_planning: การวางแผน button_login: เข้าระบบ button_submit: จัดส่งข้อมูล @@ -893,7 +892,6 @@ th: error_can_not_remove_role: This role is in use and can not be deleted. error_can_not_delete_tracker: This tracker contains issues and cannot be deleted. field_principal: Principal - label_my_page_block: My page block notice_failed_to_save_members: "Failed to save member(s): %{errors}." text_zoom_out: Zoom out text_zoom_in: Zoom in @@ -904,10 +902,8 @@ th: project_module_calendar: Calendar button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}" field_text: Text field - label_user_mail_option_only_owner: Only for things I am the owner of setting_default_notification_option: Default notification option label_user_mail_option_only_my_events: Only for things I watch or I'm involved in - label_user_mail_option_only_assigned: Only for things I am assigned to label_user_mail_option_none: No events field_member_of_group: Assignee's group field_assigned_to_role: Assignee's role @@ -964,16 +960,12 @@ th: description_project_scope: Search scope description_filter: Filter description_user_mail_notification: Mail notification settings - description_date_from: Enter start date description_message_content: Message content description_available_columns: Available Columns - description_date_range_interval: Choose range by selecting start and end date description_issue_category_reassign: Choose issue category description_search: Searchfield description_notes: Notes - description_date_range_list: Choose range from list description_choose_project: Projects - description_date_to: Enter end date description_query_sort_criteria_attribute: Sort attribute description_wiki_subpages_reassign: Choose new parent page description_selected_columns: Selected Columns @@ -1205,5 +1197,31 @@ th: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/tr.yml b/config/locales/tr.yml index ae29dd98f..f7af97b6d 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -144,6 +144,8 @@ tr: circular_dependency: "Bu ilişki döngüsel bağımlılık meydana getirecektir" cant_link_an_issue_with_a_descendant: "Bir iş, alt işlerinden birine bağlanamaz" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" models: actionview_instancetag_blank_option: Lütfen Seçin @@ -157,7 +159,7 @@ tr: general_csv_encoding: ISO-8859-9 general_pdf_fontname: freesans general_pdf_monospaced_fontname: freemono - general_first_day_of_week: '7' + general_first_day_of_week: '1' notice_account_updated: Hesap başarıyla güncelleştirildi. notice_account_invalid_credentials: Geçersiz kullanıcı ya da parola @@ -460,7 +462,6 @@ tr: label_internal: Dahili label_last_changes: "Son %{count} değişiklik" label_change_view_all: Tüm Değişiklikleri göster - label_personalize_page: Bu sayfayı kişiselleştir label_comment: Yorum label_comment_plural: Yorumlar label_x_comments: @@ -603,7 +604,6 @@ tr: label_age: Yaş label_change_properties: Özellikleri değiştir label_general: Genel - label_more: Daha fazla label_scm: KY label_plugins: Eklentiler label_ldap_authentication: LDAP Denetimi @@ -613,7 +613,6 @@ tr: label_preferences: Tercihler label_chronological_order: Tarih sırasına göre label_reverse_chronological_order: Ters tarih sırasına göre - label_planning: Planlanıyor button_login: Giriş button_submit: Gönder @@ -907,7 +906,6 @@ tr: error_can_not_remove_role: Bu rol kullanımda olduğundan silinemez. error_can_not_delete_tracker: Bu iş tipi içerisinde iş barındırdığından silinemiyor. field_principal: Temel - label_my_page_block: Kişisel sayfa bloğum notice_failed_to_save_members: "Üyeler kaydedilemiyor: %{errors}." text_zoom_out: Uzaklaş text_zoom_in: Yakınlaş @@ -918,10 +916,8 @@ tr: project_module_calendar: Takvim button_edit_associated_wikipage: "İlişkilendirilmiş Wiki sayfasını düzenle: %{page_title}" field_text: Metin alanı - label_user_mail_option_only_owner: Sadece sahibi olduğum şeyler için setting_default_notification_option: Varsayılan bildirim seçeneği label_user_mail_option_only_my_events: Sadece takip ettiğim ya da içinde olduğum şeyler için - label_user_mail_option_only_assigned: Sadece bana atanan şeyler için label_user_mail_option_none: Hiç bir şey için field_member_of_group: Atananın grubu field_assigned_to_role: Atananın rolü @@ -979,16 +975,12 @@ tr: description_project_scope: Arama kapsamı description_filter: Süzgeç description_user_mail_notification: E-posta bildirim ayarları - description_date_from: Başlangıç tarihini gir description_message_content: Mesaj içeriği description_available_columns: Kullanılabilir Sütunlar - description_date_range_interval: Başlangıç ve bitiş tarihini seçerek tarih aralığını belirleyin description_issue_category_reassign: İş kategorisini seçin description_search: Arama alanı description_notes: Notlar - description_date_range_list: Listeden tarih aralığını seçin description_choose_project: Projeler - description_date_to: Bitiş tarihini gir description_query_sort_criteria_attribute: Sıralama ölçütü description_wiki_subpages_reassign: Yeni üst sayfa seç description_selected_columns: Seçilmiş Sütunlar @@ -1216,5 +1208,31 @@ tr: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/uk.yml b/config/locales/uk.yml index cde94f8f2..ac175d7ed 100644 --- a/config/locales/uk.yml +++ b/config/locales/uk.yml @@ -9,12 +9,12 @@ uk: short: "%b %d" long: "%B %d, %Y" - day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday] - abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat] + day_names: [неділя, понеділок, вівторок, середа, четвер, пятниця, субота] + abbr_day_names: [Нд, Пн, Вт, Ср, Чт, Пт, Сб] # Don't forget the nil at the beginning; there's no such thing as a 0th month - month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December] - abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec] + month_names: [~, січня, лютого, березня, квітня, травня, червня, липня, серпня, вересня, жовтня, листопада, грудня] + abbr_month_names: [~, січ., лют., бер., квіт., трав., чер., лип., серп., вер., жовт., лист., груд.] # Used in date_select and datime_select. order: - :year @@ -27,48 +27,64 @@ uk: time: "%H:%M" short: "%d %b %H:%M" long: "%B %d, %Y %H:%M" - am: "am" - pm: "pm" + am: "ранку" + pm: "вечора" datetime: distance_in_words: - half_a_minute: "half a minute" + half_a_minute: "менше хвилини" less_than_x_seconds: - one: "less than 1 second" - other: "less than %{count} seconds" + one: "менше ніж за 1 секунду" + few: "менш %{count} секунд" + many: "менш %{count} секунд" + other: "менше ніж за %{count} секунди" x_seconds: - one: "1 second" - other: "%{count} seconds" + one: "1 секунда" + few: "%{count} секунди" + many: "%{count} секунд" + other: "%{count} секунд" less_than_x_minutes: - one: "less than a minute" - other: "less than %{count} minutes" + one: "менше ніж за хвилину" + few: "менше %{count} хвилин" + many: "менше %{count} хвилин" + other: "менш ніж за %{count} хвилин" x_minutes: - one: "1 minute" - other: "%{count} minutes" + one: "1 хвилина" + few: "%{count} хвилини" + many: "%{count} хвилин" + other: "%{count} хвилини" about_x_hours: - one: "about 1 hour" - other: "about %{count} hours" + one: "близько 1 години" + other: "близько %{count} годин" x_hours: - one: "1 hour" - other: "%{count} hours" + one: "1 година" + few: "%{count} години" + many: "%{count} годин" + other: "%{count} години" x_days: - one: "1 day" - other: "%{count} days" + one: "1 день" + few: "%{count} дня" + many: "%{count} днів" + other: "%{count} днів" about_x_months: - one: "about 1 month" - other: "about %{count} months" + one: "близько 1 місяця" + other: "близько %{count} місяців" x_months: - one: "1 month" - other: "%{count} months" + one: "1 місяць" + few: "%{count} місяця" + many: "%{count} місяців" + other: "%{count} місяців" about_x_years: - one: "about 1 year" - other: "about %{count} years" + one: "близько 1 року" + other: "близько %{count} років" over_x_years: - one: "over 1 year" - other: "over %{count} years" + one: "більше 1 року" + other: "більше %{count} років" almost_x_years: - one: "almost 1 year" - other: "almost %{count} years" + one: "майже 1 рік" + few: "майже %{count} року" + many: "майже %{count} років" + other: "майже %{count} років" number: format: @@ -82,26 +98,26 @@ uk: storage_units: format: "%n %u" units: - kb: KB - tb: TB - gb: GB + kb: КБ + tb: ТБ + gb: ГБ byte: - one: Byte - other: Bytes - mb: MB + one: Байт + other: Байтів + mb: МБ # Used in array.to_sentence. support: array: - sentence_connector: "and" + sentence_connector: "і" skip_last_comma: false activerecord: errors: template: header: - one: "1 error prohibited this %{model} from being saved" - other: "%{count} errors prohibited this %{model} from being saved" + one: "1 помилка не дозволяє зберегти %{model}" + other: "%{count} помилок не дозволяють зберегти %{model}" messages: inclusion: "немає в списку" exclusion: "зарезервовано" @@ -116,18 +132,20 @@ uk: taken: "вже використовується" not_a_number: "не є числом" not_a_date: "є недійсною датою" - greater_than: "must be greater than %{count}" - greater_than_or_equal_to: "must be greater than or equal to %{count}" - equal_to: "must be equal to %{count}" - less_than: "must be less than %{count}" - less_than_or_equal_to: "must be less than or equal to %{count}" - odd: "must be odd" - even: "must be even" + greater_than: "значення має бути більшим ніж %{count}" + greater_than_or_equal_to: "значення має бути більшим або дорівнювати %{count}" + equal_to: "значення має дорівнювати %{count}" + less_than: "значення має бути меншим ніж %{count}" + less_than_or_equal_to: "значення має бути меншим або дорівнювати %{count}" + odd: "може мати тільки непарне значення" + even: "може мати тільки парне значення" greater_than_start_date: "повинна бути пізніша за дату початку" not_same_project: "не відносяться до одного проекту" circular_dependency: "Такий зв'язок приведе до циклічної залежності" - cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" - earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + cant_link_an_issue_with_a_descendant: "Задача не може бути зв'язана зі своїми підзадачами" + earlier_than_minimum_start_date: "не може бути раніше ніж %{date} через попередні задачі" + not_a_regexp: "недопустимий регулярний вираз" + open_issue_with_closed_parent: "Відкрите завдання не може бути приєднано до закритого батьківського завдання" actionview_instancetag_blank_option: Оберіть @@ -210,7 +228,7 @@ uk: field_due_date: Дата виконання field_assigned_to: Призначена до field_priority: Пріоритет - field_fixed_version: Target version + field_fixed_version: Версія field_user: Користувач field_role: Роль field_homepage: Домашня сторінка @@ -292,9 +310,9 @@ uk: label_project_new: Новий проект label_project_plural: Проекти label_x_projects: - zero: no projects - one: 1 project - other: "%{count} projects" + zero: немає проектів + one: 1 проект + other: "%{count} проектів" label_project_all: Усі проекти label_project_latest: Останні проекти label_issue: Питання @@ -391,13 +409,13 @@ uk: label_closed_issues: закрите label_closed_issues_plural: закриті label_x_open_issues_abbr: - zero: 0 open - one: 1 open - other: "%{count} open" + zero: 0 відкрито + one: 1 відкрита + other: "%{count} відкрито" label_x_closed_issues_abbr: - zero: 0 closed - one: 1 closed - other: "%{count} closed" + zero: 0 закрито + one: 1 закрита + other: "%{count} закрито" label_total: Всього label_permissions: Права доступу label_current_status: Поточний статус @@ -416,13 +434,12 @@ uk: label_internal: Внутрішній label_last_changes: "останні %{count} змін" label_change_view_all: Проглянути всі зміни - label_personalize_page: Персоналізувати цю сторінку label_comment: Коментувати label_comment_plural: Коментарі label_x_comments: - zero: no comments - one: 1 comment - other: "%{count} comments" + zero: немає коментарів + one: 1 коментар + other: "%{count} коментарів" label_comment_add: Залишити коментар label_comment_added: Коментар додано label_comment_delete: Видалити коментарі @@ -587,7 +604,7 @@ uk: status_locked: Заблокований text_select_mail_notifications: Виберіть дії, на які відсилатиметься повідомлення на електронну пошту. - text_regexp_info: eg. ^[A-Z0-9]+$ + text_regexp_info: наприклад ^[A-Z0-9]+$ text_min_max_length_info: 0 означає відсутність заборон text_project_destroy_confirmation: Ви наполягаєте на видаленні цього проекту і всієї інформації, що відноситься до нього? text_workflow_edit: Виберіть роль і координатор для редагування послідовності дій @@ -602,8 +619,8 @@ uk: text_unallowed_characters: Заборонені символи text_comma_separated: Допустимі декілька значень (розділені комою). text_issues_ref_in_commit_messages: Посилання та зміна питань у повідомленнях до подавань - text_issue_added: "Issue %{id} has been reported by %{author}." - text_issue_updated: "Issue %{id} has been updated by %{author}." + text_issue_added: "Задача %{id} була додана %{author}." + text_issue_updated: "Задача %{id} була оновлена %{author}." text_wiki_destroy_confirmation: Ви впевнені, що хочете видалити цю wiki і весь зміст? text_issue_category_destroy_question: "Декілька питань (%{count}) призначено в цю категорію. Що ви хочете зробити?" text_issue_category_destroy_assignments: Видалити призначення категорії @@ -619,7 +636,7 @@ uk: default_tracker_feature: Властивість default_tracker_support: Підтримка default_issue_status_new: Новий - default_issue_status_in_progress: In Progress + default_issue_status_in_progress: В процесі default_issue_status_resolved: Вирішено default_issue_status_feedback: Зворотний зв'язок default_issue_status_closed: Зачинено @@ -637,571 +654,573 @@ uk: enumeration_issue_priorities: Пріоритети питань enumeration_doc_categories: Категорії документів enumeration_activities: Дії (облік часу) - text_status_changed_by_changeset: "Applied in changeset %{value}." - label_display_per_page: "Per page: %{value}" - label_issue_added: Issue added - label_issue_updated: Issue updated - setting_per_page_options: Objects per page options - notice_default_data_loaded: Default configuration successfully loaded. - error_scm_not_found: "Entry and/or revision doesn't exist in the repository." - label_associated_revisions: Associated revisions - label_document_added: Document added - label_message_posted: Message added - text_issues_destroy_confirmation: 'Are you sure you want to delete the selected issue(s) ?' - error_scm_command_failed: "An error occurred when trying to access the repository: %{value}" - setting_user_format: Users display format - label_age: Age - label_file_added: File added - label_more: More - field_default_value: Default value - label_scm: SCM - label_general: General - button_update: Update - text_select_project_modules: 'Select modules to enable for this project:' - label_change_properties: Change properties - text_load_default_configuration: Load the default configuration - text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded." - label_news_added: News added - label_repository_plural: Repositories - error_can_t_load_default_data: "Default configuration could not be loaded: %{value}" - project_module_boards: Boards - project_module_issue_tracking: Issue tracking + text_status_changed_by_changeset: "Реалізовано в редакції %{value}." + label_display_per_page: "На сторінку: %{value}" + label_issue_added: Задача додана + label_issue_updated: Задача оновлена + setting_per_page_options: Кількість записів на сторінку + notice_default_data_loaded: Конфігурація по замовчуванню була завантажена. + error_scm_not_found: "Сховище не містить записів і/чи виправлень." + label_associated_revisions: Пов'язані редакції + label_document_added: Документ додано + label_message_posted: Повідомлення додано + text_issues_destroy_confirmation: 'Ви впевнені, що хочете видалити вибрані задачі?' + error_scm_command_failed: "Помилка доступу до сховища: %{value}" + setting_user_format: Формат відображення часу + label_age: Вік + label_file_added: Файл додано + field_default_value: Значення по замовчуванню + label_scm: Тип сховища + label_general: Загальне + button_update: Оновити + text_select_project_modules: 'Виберіть модулі, які будуть використані в проекті:' + label_change_properties: Змінити властивості + text_load_default_configuration: Завантажити Конфігурацію по замовчуванню + text_no_configuration_data: "Ролі, трекери, статуси задач і оперативний план не були сконфігуровані.\nНаполегливо рекомендується завантажити конфігурацію по-замовчуванню. Ви зможете її змінити згодом." + label_news_added: Додана новина + label_repository_plural: Сховища + error_can_t_load_default_data: "Конфігурація по замовчуванню не може бути завантажена: %{value}" + project_module_boards: Форуми + project_module_issue_tracking: Задачі project_module_wiki: Wiki - project_module_files: Files - project_module_documents: Documents - project_module_repository: Repository - project_module_news: News - project_module_time_tracking: Time tracking - text_file_repository_writable: File repository writable - text_default_administrator_account_changed: Default administrator account changed - text_rmagick_available: RMagick available (optional) - button_configure: Configure - label_plugins: Plugins - label_ldap_authentication: LDAP authentication - label_downloads_abbr: D/L - label_this_month: this month - label_last_n_days: "last %{count} days" - label_all_time: all time - label_this_year: this year - label_date_range: Date range - label_last_week: last week - label_yesterday: yesterday - label_last_month: last month - label_add_another_file: Add another file - label_optional_description: Optional description - text_destroy_time_entries_question: "%{hours} hours were reported on the issues you are about to delete. What do you want to do ?" - error_issue_not_found_in_project: 'The issue was not found or does not belong to this project' - text_assign_time_entries_to_project: Assign reported hours to the project - text_destroy_time_entries: Delete reported hours - text_reassign_time_entries: 'Reassign reported hours to this issue:' - setting_activity_days_default: Days displayed on project activity - label_chronological_order: In chronological order - field_comments_sorting: Display comments - label_reverse_chronological_order: In reverse chronological order - label_preferences: Preferences - setting_display_subprojects_issues: Display subprojects issues on main projects by default - label_overall_activity: Overall activity - setting_default_projects_public: New projects are public by default - error_scm_annotate: "The entry does not exist or can not be annotated." - label_planning: Planning - text_subprojects_destroy_warning: "Its subproject(s): %{value} will be also deleted." - label_and_its_subprojects: "%{value} and its subprojects" - mail_body_reminder: "%{count} issue(s) that are assigned to you are due in the next %{days} days:" - mail_subject_reminder: "%{count} issue(s) due in the next %{days} days" - text_user_wrote: "%{value} wrote:" - label_duplicated_by: duplicated by - setting_enabled_scm: Enabled SCM - text_enumeration_category_reassign_to: 'Reassign them to this value:' - text_enumeration_destroy_question: "%{count} objects are assigned to this value." - label_incoming_emails: Incoming emails - label_generate_key: Generate a key - setting_mail_handler_api_enabled: Enable WS for incoming emails - setting_mail_handler_api_key: API key - text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/configuration.yml and restart the application to enable them." - field_parent_title: Parent page - label_issue_watchers: Watchers - button_quote: Quote - setting_sequential_project_identifiers: Generate sequential project identifiers - notice_unable_delete_version: Unable to delete version - label_renamed: renamed - label_copied: copied - setting_plain_text_mail: plain text only (no HTML) - permission_view_files: View files - permission_edit_issues: Edit issues - permission_edit_own_time_entries: Edit own time logs - permission_manage_public_queries: Manage public queries - permission_add_issues: Add issues - permission_log_time: Log spent time - permission_view_changesets: View changesets - permission_view_time_entries: View spent time - permission_manage_versions: Manage versions - permission_manage_wiki: Manage wiki - permission_manage_categories: Manage issue categories - permission_protect_wiki_pages: Protect wiki pages - permission_comment_news: Comment news - permission_delete_messages: Delete messages - permission_select_project_modules: Select project modules - permission_edit_wiki_pages: Edit wiki pages - permission_add_issue_watchers: Add watchers - permission_view_gantt: View gantt chart - permission_move_issues: Move issues - permission_manage_issue_relations: Manage issue relations - permission_delete_wiki_pages: Delete wiki pages - permission_manage_boards: Manage boards - permission_delete_wiki_pages_attachments: Delete attachments - permission_view_wiki_edits: View wiki history - permission_add_messages: Post messages - permission_view_messages: View messages - permission_manage_files: Manage files - permission_edit_issue_notes: Edit notes - permission_manage_news: Manage news - permission_view_calendar: View calendrier - permission_manage_members: Manage members - permission_edit_messages: Edit messages - permission_delete_issues: Delete issues - permission_view_issue_watchers: View watchers list - permission_manage_repository: Manage repository - permission_commit_access: Commit access - permission_browse_repository: Browse repository - permission_view_documents: View documents - permission_edit_project: Edit project - permission_add_issue_notes: Add notes - permission_save_queries: Save queries - permission_view_wiki_pages: View wiki - permission_rename_wiki_pages: Rename wiki pages - permission_edit_time_entries: Edit time logs - permission_edit_own_issue_notes: Edit own notes - setting_gravatar_enabled: Use Gravatar user icons - label_example: Example - text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped." - permission_edit_own_messages: Edit own messages - permission_delete_own_messages: Delete own messages - label_user_activity: "%{value}'s activity" - label_updated_time_by: "Updated by %{author} %{age} ago" - text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.' - setting_diff_max_lines_displayed: Max number of diff lines displayed - text_plugin_assets_writable: Plugin assets directory writable - warning_attachments_not_saved: "%{count} file(s) could not be saved." - button_create_and_continue: Create and continue - text_custom_field_possible_values_info: 'One line for each value' - label_display: Display - field_editable: Editable - setting_repository_log_display_limit: Maximum number of revisions displayed on file log - setting_file_max_size_displayed: Max size of text files displayed inline - field_watcher: Watcher - setting_openid: Allow OpenID login and registration + project_module_files: Файли + project_module_documents: Документи + project_module_repository: Сховище + project_module_news: Новини + project_module_time_tracking: Відстеження часу + text_file_repository_writable: Сховище файлів доступне для записів + text_default_administrator_account_changed: Обліковий запис адміністратора по замовчуванню змінений + text_rmagick_available: Доступно використання RMagick (опційно) + button_configure: Налаштування + label_plugins: Модулі + label_ldap_authentication: Авторизація за допомогою LDAP + label_downloads_abbr: Завантажень + label_this_month: цей місяць + label_last_n_days: "останні %{count} днів" + label_all_time: весь час + label_this_year: цей рік + label_date_range: інтервал часу + label_last_week: попередній тиждень + label_yesterday: вчора + label_last_month: попередній місяць + label_add_another_file: Додати ще один файл + label_optional_description: Опис (не обов'язково) + text_destroy_time_entries_question: "На дану задачу зареєстровано %{hours} години(ин) трудовитрат. Що Ви хочете зробити?" + error_issue_not_found_in_project: 'Задача не була знайдена або не стосується даного проекту' + text_assign_time_entries_to_project: Додати зареєстрований час до проекту + text_destroy_time_entries: Видалити зареєстрований час + text_reassign_time_entries: 'Перенести зареєстрований час на наступну задачу:' + setting_activity_days_default: Кількість днів, відображених в Діях + label_chronological_order: В хронологічному порядку + field_comments_sorting: Відображення коментарів + label_reverse_chronological_order: В зворотньому порядку + label_preferences: Переваги + setting_display_subprojects_issues: Відображення підпроектів по замовчуванню + label_overall_activity: Зведений звіт дій + setting_default_projects_public: Нові проекти є загальнодоступними + error_scm_annotate: "Коментар неможливий через перевищення максимального розміру текстового файлу." + text_subprojects_destroy_warning: "Підпроекти: %{value} також будуть видалені." + label_and_its_subprojects: "%{value} і всі підпроекти" + mail_body_reminder: "%{count} призначених на Вас задач на наступні %{days} днів:" + mail_subject_reminder: "%{count} призначених на Вас задач в найближчі %{days} дні" + text_user_wrote: "%{value} писав(ла):" + label_duplicated_by: дублюється + setting_enabled_scm: Увімкнені SCM + text_enumeration_category_reassign_to: 'Надати їм наступне значення:' + text_enumeration_destroy_question: "%{count} об'єкт(а,ів) зв'язані з цим значенням." + label_incoming_emails: Прийом повідомлень + label_generate_key: Згенерувати ключ + setting_mail_handler_api_enabled: Увімкнути веб-сервіс для вхідних повідомлень + setting_mail_handler_api_key: API ключ + text_email_delivery_not_configured: "Параметри роботи з поштовим сервером не налаштовані і функція сповіщення по email не активна.\nНалаштувати параметри для Вашого SMTP-сервера Ви можете в файлі config/configuration.yml. Для застосування змін перезапустіть програму." + field_parent_title: Домашня сторінка + label_issue_watchers: Спостерігачі + button_quote: Цитувати + setting_sequential_project_identifiers: Генерувати послідовні ідентифікатори проектів + notice_unable_delete_version: Неможливо видалити версію. + label_renamed: перейменовано + label_copied: скопійовано в + setting_plain_text_mail: Тільки простий текст (без HTML) + permission_view_files: Перегляд файлів + permission_edit_issues: Редагування задач + permission_edit_own_time_entries: Редагування власного обліку часу + permission_manage_public_queries: Управління загальними запитами + permission_add_issues: Додавання задач + permission_log_time: Облік трудовитрат + permission_view_changesets: Перегляд змін сховища + permission_view_time_entries: Перегляд трудовитрат + permission_manage_versions: Управління версіями + permission_manage_wiki: Управління wiki + permission_manage_categories: Управління категоріями задач + permission_protect_wiki_pages: Блокування wiki-сторінок + permission_comment_news: Коментування новин + permission_delete_messages: Видалення повідомлень + permission_select_project_modules: Вибір модулів проекту + permission_edit_wiki_pages: Редагування wiki-сторінок + permission_add_issue_watchers: Додати спостерігачів + permission_view_gantt: Перегляд діаграми Ганта + permission_move_issues: Перенесення задач + permission_manage_issue_relations: Управління зв'язуванням задач + permission_delete_wiki_pages: Видалення wiki-сторінок + permission_manage_boards: Управління форумами + permission_delete_wiki_pages_attachments: Видалення прикріплених файлів + permission_view_wiki_edits: Перегляд історії wiki + permission_add_messages: Відправка повідомлень + permission_view_messages: Перегляд повідомлень + permission_manage_files: Управління файлами + permission_edit_issue_notes: Редагування нотаток + permission_manage_news: Управління новинами + permission_view_calendar: Перегляд календаря + permission_manage_members: Управління учасниками + permission_edit_messages: Редагування повідомлень + permission_delete_issues: Видалення задач + permission_view_issue_watchers: Перегляд списку спостерігачів + permission_manage_repository: Управління сховищем + permission_commit_access: Зміна файлів в сховищі + permission_browse_repository: Перегляд сховища + permission_view_documents: Перегляд документів + permission_edit_project: Редагування проектів + permission_add_issue_notes: Додавання нотаток + permission_save_queries: Збереження запитів + permission_view_wiki_pages: Перегляд wiki + permission_rename_wiki_pages: Перейменування wiki-сторінок + permission_edit_time_entries: Редагування обліку часу + permission_edit_own_issue_notes: Редагування власних нотаток + setting_gravatar_enabled: Використовувати аватар користувача із Gravatar + label_example: Зразок + text_repository_usernames_mapping: "Виберіть або оновіть користувача Redmine, пов'язаного зі знайденими іменами в журналі сховища.\nКористувачі з одинаковими іменами або email в Redmine і сховищі пов'язуються автоматично." + permission_edit_own_messages: Редагування власних повідомлень + permission_delete_own_messages: Видалення власних повідомлень + label_user_activity: "Дії користувача %{value}" + label_updated_time_by: "Оновлено %{author} %{age} назад" + text_diff_truncated: '... Цей diff обмежений, так як перевищує максимальний розмір, що може бути відображений.' + setting_diff_max_lines_displayed: Максимальна кількість рядків для diff + text_plugin_assets_writable: Каталог ресурсів модулів доступний для запису + warning_attachments_not_saved: "%{count} файл(ів) неможливо зберегти." + button_create_and_continue: Створити та продовжити + text_custom_field_possible_values_info: 'По одному значенню в кожному рядку' + label_display: Відображення + field_editable: Доступно до редагування + setting_repository_log_display_limit: Максимальна кількість редакцій, відображених в журналі змін + setting_file_max_size_displayed: Максимальний розмір текстового файлу для відображення + field_watcher: Спостерігач + setting_openid: Дозволити OpenID для входу та реєстрації field_identity_url: OpenID URL - label_login_with_open_id_option: or login with OpenID - field_content: Content - label_descending: Descending - label_sort: Sort - label_ascending: Ascending - label_date_from_to: From %{start} to %{end} + label_login_with_open_id_option: або війти з допомогою OpenID + field_content: Вміст + label_descending: За спаданням + label_sort: Сортувати + label_ascending: За зростанням + label_date_from_to: З %{start} по %{end} label_greater_or_equal: ">=" label_less_or_equal: <= - text_wiki_page_destroy_question: This page has %{descendants} child page(s) and descendant(s). What do you want to do? - text_wiki_page_reassign_children: Reassign child pages to this parent page - text_wiki_page_nullify_children: Keep child pages as root pages - text_wiki_page_destroy_children: Delete child pages and all their descendants - setting_password_min_length: Minimum password length - field_group_by: Group results by - mail_subject_wiki_content_updated: "'%{id}' wiki page has been updated" - label_wiki_content_added: Wiki page added - mail_subject_wiki_content_added: "'%{id}' wiki page has been added" - mail_body_wiki_content_added: The '%{id}' wiki page has been added by %{author}. - label_wiki_content_updated: Wiki page updated - mail_body_wiki_content_updated: The '%{id}' wiki page has been updated by %{author}. - permission_add_project: Create project - setting_new_project_user_role_id: Role given to a non-admin user who creates a project - label_view_all_revisions: View all revisions - label_tag: Tag - label_branch: Branch - error_no_tracker_in_project: No tracker is associated to this project. Please check the Project settings. - error_no_default_issue_status: No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses"). - text_journal_changed: "%{label} changed from %{old} to %{new}" - text_journal_set_to: "%{label} set to %{value}" - text_journal_deleted: "%{label} deleted (%{old})" - label_group_plural: Groups - label_group: Group - label_group_new: New group - label_time_entry_plural: Spent time - text_journal_added: "%{label} %{value} added" - field_active: Active - enumeration_system_activity: System Activity - permission_delete_issue_watchers: Delete watchers - version_status_closed: closed - version_status_locked: locked - version_status_open: open - error_can_not_reopen_issue_on_closed_version: An issue assigned to a closed version can not be reopened - label_user_anonymous: Anonymous - button_move_and_follow: Move and follow - setting_default_projects_modules: Default enabled modules for new projects - setting_gravatar_default: Default Gravatar image - field_sharing: Sharing - label_version_sharing_hierarchy: With project hierarchy - label_version_sharing_system: With all projects - label_version_sharing_descendants: With subprojects - label_version_sharing_tree: With project tree - label_version_sharing_none: Not shared - error_can_not_archive_project: This project can not be archived - button_duplicate: Duplicate - button_copy_and_follow: Copy and follow - label_copy_source: Source - setting_issue_done_ratio: Calculate the issue done ratio with - setting_issue_done_ratio_issue_status: Use the issue status - error_issue_done_ratios_not_updated: Issue done ratios not updated. - error_workflow_copy_target: Please select target tracker(s) and role(s) - setting_issue_done_ratio_issue_field: Use the issue field - label_copy_same_as_target: Same as target - label_copy_target: Target - notice_issue_done_ratios_updated: Issue done ratios updated. - error_workflow_copy_source: Please select a source tracker or role - label_update_issue_done_ratios: Update issue done ratios - setting_start_of_week: Start calendars on - permission_view_issues: View Issues - label_display_used_statuses_only: Only display statuses that are used by this tracker - label_revision_id: Revision %{value} - label_api_access_key: API access key - label_api_access_key_created_on: API access key created %{value} ago - label_feeds_access_key: Atom access key - notice_api_access_key_reseted: Your API access key was reset. - setting_rest_api_enabled: Enable REST web service - label_missing_api_access_key: Missing an API access key - label_missing_feeds_access_key: Missing a Atom access key - button_show: Show - text_line_separated: Multiple values allowed (one line for each value). - setting_mail_handler_body_delimiters: Truncate emails after one of these lines - permission_add_subprojects: Create subprojects - label_subproject_new: New subproject + text_wiki_page_destroy_question: Ця сторінка має %{descendants} дочірних сторінок і їх нащадків. Що Ви хочете зробити? + text_wiki_page_reassign_children: Переоприділити дочірні сторінки та поточту сторінку + text_wiki_page_nullify_children: Зробити дочірні сторінки головними сторінками + text_wiki_page_destroy_children: Видалити дочірні сторінки і всіх їх нащадків + setting_password_min_length: Мінімальна довжина паролю + field_group_by: Групувати результати по + mail_subject_wiki_content_updated: "Wiki-сторінка '%{id}' була оновлена" + label_wiki_content_added: Wiki-сторінка додана + mail_subject_wiki_content_added: "Wiki-сторінка '%{id}' була додана" + mail_body_wiki_content_added: "%{author} додав(ла) wiki-сторінку %{id}." + label_wiki_content_updated: Wiki-сторінка оновлена + mail_body_wiki_content_updated: "%{author} оновив(ла) wiki-сторінку %{id}." + permission_add_project: Створення проекту + setting_new_project_user_role_id: Роль, що призначається користувачу, створившому проект + label_view_all_revisions: Показати всі ревізії + label_tag: Мітка + label_branch: Гілка + error_no_tracker_in_project: З цим проектом не асоційований ні один трекер. Будь ласка, перевірте налаштування проекту. + error_no_default_issue_status: Не визначений статус задач за замовчуванням. Будь ласка, перевірте налаштування (див. "Адміністрування -> Статуси задач"). + text_journal_changed: "Параметр %{label} змінився з %{old} на %{new}" + text_journal_set_to: "Параметр %{label} змінився на %{value}" + text_journal_deleted: "Значення %{old} параметру %{label} видалено" + label_group_plural: Групи + label_group: Група + label_group_new: Нова група + label_time_entry_plural: Трудовитрати + text_journal_added: "%{label} %{value} доданий" + field_active: Активно + enumeration_system_activity: Системне + permission_delete_issue_watchers: Видалення спостерігачів + version_status_closed: закритий + version_status_locked: заблокований + version_status_open: відкритий + error_can_not_reopen_issue_on_closed_version: Задача, призначена до закритої версії, не зможе бути відкрита знову + label_user_anonymous: Анонім + button_move_and_follow: Перемістити і перейти + setting_default_projects_modules: Включені по замовчуванню модулі для нових проектів + setting_gravatar_default: Зображення Gravatar за замовчуванням + field_sharing: Сумісне використання + label_version_sharing_hierarchy: З ієрархією пректів + label_version_sharing_system: З усіма проектами + label_version_sharing_descendants: З підпроектами + label_version_sharing_tree: З деревом проектів + label_version_sharing_none: Без сумісного доступу + error_can_not_archive_project: Цей проект не може бути заархівований + button_duplicate: Дублювати + button_copy_and_follow: Скопіювати та продовжити + label_copy_source: Джерело + setting_issue_done_ratio: Розраховувати готовність задачі з допомогою поля + setting_issue_done_ratio_issue_status: Статус задачі + error_issue_done_ratios_not_updated: Параметр готовність задач не оновлений. + error_workflow_copy_target: Оберіть цільові трекери і ролі + setting_issue_done_ratio_issue_field: Готовність задачі + label_copy_same_as_target: Те саме, що і у цілі + label_copy_target: Ціль + notice_issue_done_ratios_updated: Параметр «готовність» оновлений. + error_workflow_copy_source: Будь ласка, виберіть початковий трекер або роль + label_update_issue_done_ratios: Оновити готовність задач + setting_start_of_week: День початку тижня + permission_view_issues: Перегляд задач + label_display_used_statuses_only: Відображати тільки ті статуси, які використовуються в цьому трекері + label_revision_id: Ревізія %{value} + label_api_access_key: Ключ доступу до API + label_api_access_key_created_on: Ключ доступу до API створено %{value} назад + label_feeds_access_key: Ключ доступу до Atom + notice_api_access_key_reseted: Ваш ключ доступу до API був скинутий. + setting_rest_api_enabled: Включити веб-сервіс REST + label_missing_api_access_key: Відсутній ключ доступу до API + label_missing_feeds_access_key: Відсутній ключ доступу до Atom + button_show: Показати + text_line_separated: Дозволено кілька значень (по одному значенню в рядок). + setting_mail_handler_body_delimiters: Урізати лист після одного з цих рядків + permission_add_subprojects: Створення підпроектів + label_subproject_new: Новий підпроект text_own_membership_delete_confirmation: |- - You are about to remove some or all of your permissions and may no longer be able to edit this project after that. - Are you sure you want to continue? - label_close_versions: Close completed versions - label_board_sticky: Sticky - label_board_locked: Locked - permission_export_wiki_pages: Export wiki pages - setting_cache_formatted_text: Cache formatted text - permission_manage_project_activities: Manage project activities - error_unable_delete_issue_status: Unable to delete issue status - label_profile: Profile - permission_manage_subtasks: Manage subtasks - field_parent_issue: Parent task - label_subtask_plural: Subtasks - label_project_copy_notifications: Send email notifications during the project copy - error_can_not_delete_custom_field: Unable to delete custom field - error_unable_to_connect: Unable to connect (%{value}) - error_can_not_remove_role: This role is in use and can not be deleted. - error_can_not_delete_tracker: This tracker contains issues and cannot be deleted. - field_principal: Principal - label_my_page_block: My page block - notice_failed_to_save_members: "Failed to save member(s): %{errors}." - text_zoom_out: Zoom out - text_zoom_in: Zoom in - notice_unable_delete_time_entry: Unable to delete time log entry. - label_overall_spent_time: Overall spent time - field_time_entries: Log time - project_module_gantt: Gantt - project_module_calendar: Calendar - button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}" - field_text: Text field - label_user_mail_option_only_owner: Only for things I am the owner of - setting_default_notification_option: Default notification option - label_user_mail_option_only_my_events: Only for things I watch or I'm involved in - label_user_mail_option_only_assigned: Only for things I am assigned to - label_user_mail_option_none: No events - field_member_of_group: Assignee's group - field_assigned_to_role: Assignee's role - notice_not_authorized_archived_project: The project you're trying to access has been archived. - label_principal_search: "Search for user or group:" - label_user_search: "Search for user:" - field_visible: Visible - setting_commit_logtime_activity_id: Activity for logged time - text_time_logged_by_changeset: Applied in changeset %{value}. - setting_commit_logtime_enabled: Enable time logging - notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max}) - setting_gantt_items_limit: Maximum number of items displayed on the gantt chart - field_warn_on_leaving_unsaved: Warn me when leaving a page with unsaved text - text_warn_on_leaving_unsaved: The current page contains unsaved text that will be lost if you leave this page. - label_my_queries: My custom queries - text_journal_changed_no_detail: "%{label} updated" - label_news_comment_added: Comment added to a news - button_expand_all: Expand all - button_collapse_all: Collapse all - label_additional_workflow_transitions_for_assignee: Additional transitions allowed when the user is the assignee - label_additional_workflow_transitions_for_author: Additional transitions allowed when the user is the author - label_bulk_edit_selected_time_entries: Bulk edit selected time entries - text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies)? - label_role_anonymous: Anonymous - label_role_non_member: Non member - label_issue_note_added: Note added - label_issue_status_updated: Status updated - label_issue_priority_updated: Priority updated - label_issues_visibility_own: Issues created by or assigned to the user - field_issues_visibility: Issues visibility - label_issues_visibility_all: All issues - permission_set_own_issues_private: Set own issues public or private - field_is_private: Private - permission_set_issues_private: Set issues public or private - label_issues_visibility_public: All non private issues - text_issues_destroy_descendants_confirmation: This will also delete %{count} subtask(s). - field_commit_logs_encoding: Commit messages encoding - field_scm_path_encoding: Path encoding - text_scm_path_encoding_note: "Default: UTF-8" - field_path_to_repository: Path to repository - field_root_directory: Root directory - field_cvs_module: Module + Ви збираєтесь видалити деякі або всі права, через що можуть зникнути права на редагування цього проекту. + Ви впевнені що хочете продовжити? + label_close_versions: Закрити завершені версії + label_board_sticky: Прикріплена + label_board_locked: Заблокована + permission_export_wiki_pages: Експорт wiki-сторінок + setting_cache_formatted_text: Кешувати форматований текст + permission_manage_project_activities: Управління типами дій для проекту + error_unable_delete_issue_status: Неможливо видалити статус задачі + label_profile: Профіль + permission_manage_subtasks: Управління підзадачами + field_parent_issue: Батьківська задача + label_subtask_plural: Підзадачі + label_project_copy_notifications: Відправляти сповіщення по електронній пошті при копіюванні проекту + error_can_not_delete_custom_field: Неможливо видалити налаштовуване поле + error_unable_to_connect: Неможливо підключитись (%{value}) + error_can_not_remove_role: Ця роль використовується і не може бути видалена. + error_can_not_delete_tracker: Цей трекер містить задачі і не може бути видалений. + field_principal: Ім'я + notice_failed_to_save_members: "Не вдалось зберегти учасника(ів): %{errors}." + text_zoom_out: Віддалити + text_zoom_in: Наблизити + notice_unable_delete_time_entry: Неможливо видалити запис журналу. + label_overall_spent_time: Всього витраченого часу (трудовитрати) + field_time_entries: Видимість трудовитрат + project_module_gantt: Діаграма Ганта + project_module_calendar: Календар + button_edit_associated_wikipage: "Редагувати пов'язану Wiki сторінку: %{page_title}" + field_text: Текстове поле + setting_default_notification_option: Спосіб сповіщення за замовчуванням + label_user_mail_option_only_my_events: Тільки для задач, які я відслідковую чи беру участь + label_user_mail_option_none: Немає подій + field_member_of_group: Група виконавця + field_assigned_to_role: Роль виконавця + notice_not_authorized_archived_project: Даний проект заархівований. + label_principal_search: "Знайти користувача або групу:" + label_user_search: "Знайти користувача:" + field_visible: Видимий + setting_commit_logtime_activity_id: Для для обліку часу + text_time_logged_by_changeset: Взято до уваги в редакції %{value}. + setting_commit_logtime_enabled: Включити облік часу + notice_gantt_chart_truncated: Діаграма буде обрізана, так як перевищено максимальну к-сть елементів для відображення (%{max}) + setting_gantt_items_limit: Максимальна к-сть елементів для відображення на діаграмі Ганта + field_warn_on_leaving_unsaved: Попереджувати при закритті сторінки з незбереженим текстом + text_warn_on_leaving_unsaved: Дана сторінка містить незбережений текст, який буде втрачено при закритті сторінки. + label_my_queries: Мої збережені запити + text_journal_changed_no_detail: "%{label} оновлено" + label_news_comment_added: Добавлено новий коментар до новини + button_expand_all: Розвернути все + button_collapse_all: Звернути все + label_additional_workflow_transitions_for_assignee: Додаткові переходи дозволені користувачу, який є виконавцем + label_additional_workflow_transitions_for_author: Додаткові переходи дозволені користувачу, який є автором + label_bulk_edit_selected_time_entries: Масова зміна вибраних записів трудовитрат + text_time_entries_destroy_confirmation: Ви впевнені, що хочете видалити вибрати трудовитрати? + label_role_anonymous: Анонім + label_role_non_member: Не учасник + label_issue_note_added: Примітку додано + label_issue_status_updated: Статус оновлено + label_issue_priority_updated: Пріоритет оновлено + label_issues_visibility_own: Створені або призначені задачі для користувача + field_issues_visibility: Видимість задач + label_issues_visibility_all: Всі задачі + permission_set_own_issues_private: Встановити видимість (повна/чаткова) для власних задач + field_is_private: Приватна + permission_set_issues_private: Встановити видимість (повна/чаткова) для задач + label_issues_visibility_public: Тільки загальні задачі + text_issues_destroy_descendants_confirmation: Також буде видалено %{count} задача(і). + field_commit_logs_encoding: Кодування коментарів у сховищі + field_scm_path_encoding: Кодування шляху + text_scm_path_encoding_note: "По замовчуванню: UTF-8" + field_path_to_repository: Шлях до сховища + field_root_directory: Коренева директорія + field_cvs_module: Модуль field_cvsroot: CVSROOT - text_mercurial_repository_note: Local repository (e.g. /hgrepo, c:\hgrepo) - text_scm_command: Command - text_scm_command_version: Version - label_git_report_last_commit: Report last commit for files and directories - notice_issue_successful_create: Issue %{id} created. - label_between: between - setting_issue_group_assignment: Allow issue assignment to groups - label_diff: diff - text_git_repository_note: Repository is bare and local (e.g. /gitrepo, c:\gitrepo) - description_query_sort_criteria_direction: Sort direction - description_project_scope: Search scope - description_filter: Filter - description_user_mail_notification: Mail notification settings - description_date_from: Enter start date - description_message_content: Message content - description_available_columns: Available Columns - description_date_range_interval: Choose range by selecting start and end date - description_issue_category_reassign: Choose issue category - description_search: Searchfield - description_notes: Notes - description_date_range_list: Choose range from list - description_choose_project: Projects - description_date_to: Enter end date - description_query_sort_criteria_attribute: Sort attribute - description_wiki_subpages_reassign: Choose new parent page - description_selected_columns: Selected Columns - label_parent_revision: Parent - label_child_revision: Child - error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size. - setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues - button_edit_section: Edit this section - setting_repositories_encodings: Attachments and repositories encodings - description_all_columns: All Columns - button_export: Export - label_export_options: "%{export_format} export options" - error_attachment_too_big: This file cannot be uploaded because it exceeds the maximum allowed file size (%{max_size}) - notice_failed_to_save_time_entries: "Failed to save %{count} time entrie(s) on %{total} selected: %{ids}." + text_mercurial_repository_note: Локальне сховище (наприклад. /hgrepo, c:\hgrepo) + text_scm_command: Команда + text_scm_command_version: Версія + label_git_report_last_commit: Зазначати останні зміни для файлів та директорій + notice_issue_successful_create: Задача %{id} створена. + label_between: між + setting_issue_group_assignment: Надавати доступ до створення задача групам користувачів + label_diff: різниця + text_git_repository_note: Сховище пусте та локальнеl (тобто. /gitrepo, c:\gitrepo) + description_query_sort_criteria_direction: Порядок сортування + description_project_scope: Область пошуку + description_filter: Фільтр + description_user_mail_notification: Настройки поштових сповіщень + description_message_content: Зміст повідомлення + description_available_columns: Доступні колонки + description_issue_category_reassign: Виберіть категорію задачі + description_search: Поле пошуку + description_notes: Примітки + description_choose_project: Проекти + description_query_sort_criteria_attribute: Критерій сортування + description_wiki_subpages_reassign: Виберіть батьківську сторінку + description_selected_columns: Вибрані колонки + label_parent_revision: Батьківський + label_child_revision: Підпорядкований + error_scm_annotate_big_text_file: Неможливо додати коментарій через перевищення максимального розміру текстового файлу. + setting_default_issue_start_date_to_creation_date: Використовувати поточну дату, як дату початку нових задач + button_edit_section: Редагувати дану секцію + setting_repositories_encodings: Кодування вкладень та сховищ + description_all_columns: Всі колонки + button_export: Експорт + label_export_options: "%{export_format} параметри екпортування" + error_attachment_too_big: Неможливо завантажити файл, він перевищує максимальний дозволений розмір (%{max_size}) + notice_failed_to_save_time_entries: "Не вдалось зберегти %{count} трудовитрати %{total} вибраних: %{ids}." label_x_issues: zero: 0 Питання one: 1 Питання other: "%{count} Питання" - label_repository_new: New repository - field_repository_is_default: Main repository - label_copy_attachments: Copy attachments + label_repository_new: Нове сховище + field_repository_is_default: Сховище за замовчуванням + label_copy_attachments: Скопіювати вкладення label_item_position: "%{position}/%{count}" - label_completed_versions: Completed versions - text_project_identifier_info: Only lower case letters (a-z), numbers, dashes and underscores are allowed.
    Once saved, the identifier cannot be changed. - field_multiple: Multiple values - setting_commit_cross_project_ref: Allow issues of all the other projects to be referenced and fixed - text_issue_conflict_resolution_add_notes: Add my notes and discard my other changes - text_issue_conflict_resolution_overwrite: Apply my changes anyway (previous notes will be kept but some changes may be overwritten) - notice_issue_update_conflict: The issue has been updated by an other user while you were editing it. - text_issue_conflict_resolution_cancel: Discard all my changes and redisplay %{link} - permission_manage_related_issues: Manage related issues - field_auth_source_ldap_filter: LDAP filter - label_search_for_watchers: Search for watchers to add - notice_account_deleted: "Ваш обліковій запис повністю видалений" - setting_unsubscribe: "Дозволити користувачам видаляти свої облікові записи" - button_delete_my_account: "Видалити мій обліковий запис" - text_account_destroy_confirmation: "Ваш обліковий запис буде повністю видалений без можливості відновлення.\nВи певні, что бажаете продовжити?" - error_session_expired: Your session has expired. Please login again. - text_session_expiration_settings: "Warning: changing these settings may expire the current sessions including yours." - setting_session_lifetime: Session maximum lifetime - setting_session_timeout: Session inactivity timeout - label_session_expiration: Session expiration - permission_close_project: Close / reopen the project - label_show_closed_projects: View closed projects - button_close: Close - button_reopen: Reopen - project_status_active: active - project_status_closed: closed - project_status_archived: archived - text_project_closed: This project is closed and read-only. - notice_user_successful_create: User %{id} created. - field_core_fields: Standard fields - field_timeout: Timeout (in seconds) - setting_thumbnails_enabled: Display attachment thumbnails - setting_thumbnails_size: Thumbnails size (in pixels) - label_status_transitions: Status transitions - label_fields_permissions: Fields permissions - label_readonly: Read-only - label_required: Required - text_repository_identifier_info: Only lower case letters (a-z), numbers, dashes and underscores are allowed.
    Once saved, the identifier cannot be changed. - field_board_parent: Parent forum - label_attribute_of_project: Project's %{name} - label_attribute_of_author: Author's %{name} - label_attribute_of_assigned_to: Assignee's %{name} - label_attribute_of_fixed_version: Target version's %{name} - label_copy_subtasks: Copy subtasks - label_copied_to: copied to - label_copied_from: copied from - label_any_issues_in_project: any issues in project - label_any_issues_not_in_project: any issues not in project - field_private_notes: Private notes - permission_view_private_notes: View private notes - permission_set_notes_private: Set notes as private - label_no_issues_in_project: no issues in project + label_completed_versions: Завершені версії + text_project_identifier_info: Допускаються тільки рядкові малі букви (a-z), цифри, тире та підкреслення (нижнє тире).
    Після збереження ідентифікатор заборонено редагувати. + field_multiple: Множинні значення + setting_commit_cross_project_ref: Дозволяти посилання та редагування задач у всіх інших проектах + text_issue_conflict_resolution_add_notes: Додати мої примітки та відмовитись від моїх змін + text_issue_conflict_resolution_overwrite: Застосувати мої зміни (всі попередні примітки будуть збережені, але деякі зміни зможуть бути перезаписані) + notice_issue_update_conflict: Хтось змінив задачу, поки ви її редагували + text_issue_conflict_resolution_cancel: Скасувати мої зміни та показати та повторно показати задачу %{link} + permission_manage_related_issues: Управління пов'язаними задачами + field_auth_source_ldap_filter: Фільтр LDAP + label_search_for_watchers: Знайти спостерігачів + notice_account_deleted: "Ваш обліковій запис повністю видалений" + setting_unsubscribe: "Дозволити користувачам видаляти свої облікові записи" + button_delete_my_account: "Видалити мій обліковий запис" + text_account_destroy_confirmation: "Ваш обліковий запис буде повністю видалений без можливості відновлення.\nВи впевнені, що бажаете продовжити?" + error_session_expired: Сеанс вичерпано. Будь ласка, ввійдіть ще раз. + text_session_expiration_settings: "Увага!: зміна даних налаштувань може спричинити завершення поточного сеансу, включаючи поточний." + setting_session_lifetime: Максимальна тривалість сеансу + setting_session_timeout: Таймаут сеансу + label_session_expiration: Термін закінчення сеансу + permission_close_project: Закривати/відкривати проекти + label_show_closed_projects: Переглянути закриті проекти + button_close: Закрити + button_reopen: Відкрити + project_status_active: Відкриті(ий) + project_status_closed: Закриті(ий) + project_status_archived: Заархівовані(ий) + text_project_closed: Проект закрито. Доступний лише в режимі читання. + notice_user_successful_create: Користувача %{id} створено. + field_core_fields: Стандартні поля + field_timeout: Таймаут (в секундах) + setting_thumbnails_enabled: Відображати попередній перегляд для вкладень + setting_thumbnails_size: Розмір попереднього перегляду (в пікселях) + label_status_transitions: Статус-переходи + label_fields_permissions: Права на редагування полів + label_readonly: Тільки для перегляду + label_required: Обов'язкове + text_repository_identifier_info: Допускаються тільки рядкові малі букви (a-z), цифри, тире та підкреслення (нижнє тире).
    Після збереження ідентифікатор заборонено редагувати. + field_board_parent: Батьківський форум + label_attribute_of_project: Проект %{name} + label_attribute_of_author: Ім'я автора %{name} + label_attribute_of_assigned_to: Призначено %{name} + label_attribute_of_fixed_version: Версія %{name} + label_copy_subtasks: Скопіювати підзадачі + label_copied_to: Скопійовано в + label_copied_from: Скопійовано з + label_any_issues_in_project: будь-які задачі в проекті + label_any_issues_not_in_project: будь-які задачі не в проекті + field_private_notes: Приватні коментарі + permission_view_private_notes: Перегляд приватних коментарів + permission_set_notes_private: Розміщення приватних коментарів + label_no_issues_in_project: в проекті немає задач label_any: Усі - label_last_n_weeks: last %{count} weeks - setting_cross_project_subtasks: Allow cross-project subtasks - label_cross_project_descendants: With subprojects - label_cross_project_tree: With project tree - label_cross_project_hierarchy: With project hierarchy - label_cross_project_system: With all projects - button_hide: Hide - setting_non_working_week_days: Non-working days - label_in_the_next_days: in the next - label_in_the_past_days: in the past - label_attribute_of_user: User's %{name} - text_turning_multiple_off: If you disable multiple values, multiple values will be - removed in order to preserve only one value per item. - label_attribute_of_issue: Issue's %{name} - permission_add_documents: Add documents - permission_edit_documents: Edit documents - permission_delete_documents: Delete documents - label_gantt_progress_line: Progress line - setting_jsonp_enabled: Enable JSONP support - field_inherit_members: Inherit members - field_closed_on: Closed - field_generate_password: Generate password - setting_default_projects_tracker_ids: Default trackers for new projects + label_last_n_weeks: минулий(і) %{count} тиждень(ні) + setting_cross_project_subtasks: Дозволити підзадачі між проектами + label_cross_project_descendants: З підпроектами + label_cross_project_tree: З деревом проектів + label_cross_project_hierarchy: З ієрархією проектів + label_cross_project_system: З усіма проектами + button_hide: Сховати + setting_non_working_week_days: Неробочі дні + label_in_the_next_days: в наступні дні + label_in_the_past_days: минулі дні + label_attribute_of_user: Користувач %{name} + text_turning_multiple_off: Якщо відключити множинні значення, зайві значення будуть видалені зі списку, так аби залишилось тільки по одному значенню. + label_attribute_of_issue: Задача %{name} + permission_add_documents: Додати документи + permission_edit_documents: Редагувати документи + permission_delete_documents: Видалити документи + label_gantt_progress_line: Лінія прогресу + setting_jsonp_enabled: Включити JSONP підтримку + field_inherit_members: Наслідувати учасників + field_closed_on: Закрито + field_generate_password: Створити пароль + setting_default_projects_tracker_ids: Трекери по замовчуванню для нових проектів label_total_time: Всього - text_scm_config: You can configure your SCM commands in config/configuration.yml. Please restart the application after editing it. - text_scm_command_not_available: SCM command is not available. Please check settings on the administration panel. - setting_emails_header: Email header - notice_account_not_activated_yet: You haven't activated your account yet. If you want - to receive a new activation email, please click this link. - notice_account_locked: Your account is locked. - label_hidden: Hidden - label_visibility_private: to me only - label_visibility_roles: to these roles only - label_visibility_public: to any users - field_must_change_passwd: Must change password at next logon - notice_new_password_must_be_different: The new password must be different from the - current password - setting_mail_handler_excluded_filenames: Exclude attachments by name - text_convert_available: ImageMagick convert available (optional) - label_link: Link - label_only: only - label_drop_down_list: drop-down list - label_checkboxes: checkboxes - label_link_values_to: Link values to URL - setting_force_default_language_for_anonymous: Force default language for anonymous - users - setting_force_default_language_for_loggedin: Force default language for logged-in - users - label_custom_field_select_type: Select the type of object to which the custom field - is to be attached - label_issue_assigned_to_updated: Assignee updated - label_check_for_updates: Check for updates - label_latest_compatible_version: Latest compatible version - label_unknown_plugin: Unknown plugin - label_radio_buttons: radio buttons - label_group_anonymous: Anonymous users - label_group_non_member: Non member users - label_add_projects: Add projects - field_default_status: Default status - text_subversion_repository_note: 'Examples: file:///, http://, https://, svn://, svn+[tunnelscheme]://' - field_users_visibility: Users visibility - label_users_visibility_all: All active users - label_users_visibility_members_of_visible_projects: Members of visible projects - label_edit_attachments: Edit attached files - setting_link_copied_issue: Link issues on copy - label_link_copied_issue: Link copied issue - label_ask: Ask - label_search_attachments_yes: Search attachment filenames and descriptions - label_search_attachments_no: Do not search attachments - label_search_attachments_only: Search attachments only - label_search_open_issues_only: Open issues only + text_scm_config: Ви можете налаштувати команди SCM в файлі config/configuration.yml. Будь ласка, перезавантажте додаток після редагування даного файлу. + text_scm_command_not_available: SCM команада недоступна. Будь ласка, перевірте налаштування в адміністративній панелі. + setting_emails_header: Заголовок листа + notice_account_not_activated_yet: Поки що ви не маєте активованих облікових записів. Для того аби отримати лист з активацією, перейдіть по click this link. + notice_account_locked: Ваш обліковий запис заблоковано. + label_hidden: Схований + label_visibility_private: тільки для мене + label_visibility_roles: тільки для даних ролей + label_visibility_public: всім користувачам + field_must_change_passwd: Змінити пароль при наступному вході + notice_new_password_must_be_different: Новий пароль повинен відрізнятись від існуючого + setting_mail_handler_excluded_filenames: Виключити вкладення по імені + text_convert_available: ImageMagick використання доступно (опціонально) + label_link: Посилання + label_only: тільки + label_drop_down_list: випадаючий список + label_checkboxes: чекбокси + label_link_values_to: Значення посилань для URL + setting_force_default_language_for_anonymous: Не визначати мову для анонімних користувачів + setting_force_default_language_for_loggedin: Не визначати мову для зареєстрованих користувачів + label_custom_field_select_type: Виберіть тип об'єкта, для якого буде створено поле для налаштування + label_issue_assigned_to_updated: Виконавець оновлений + label_check_for_updates: Перевірити оновлення + label_latest_compatible_version: Остання сумісна версія + label_unknown_plugin: Невідомий плагін + label_radio_buttons: радіо-кнопки + label_group_anonymous: Анонімні користувачі + label_group_non_member: Користувачі неучасники + label_add_projects: Додати проекти + field_default_status: Статус по замовчуванню + text_subversion_repository_note: 'наприклад:///, http://, https://, svn://, svn+[tunnelscheme]://' + field_users_visibility: Видимість користувачів + label_users_visibility_all: Всі активні користувачі + label_users_visibility_members_of_visible_projects: Учасники видимих проектів + label_edit_attachments: Редагувати прикріплені файли + setting_link_copied_issue: Зв'язати задачі при копіюванні + label_link_copied_issue: Зв'язати скопійовану задачу + label_ask: Спитати + label_search_attachments_yes: Шукати в назвах прикріплених файлів та описах + label_search_attachments_no: Не шукати в прикріплених файлах + label_search_attachments_only: Шукати тільки в прикріплених файлах + label_search_open_issues_only: Тільки у відкритих задачах field_address: Ел. пошта - setting_max_additional_emails: Maximum number of additional email addresses + setting_max_additional_emails: Максимальна кількість додаткрвих email адрес label_email_address_plural: Emails - label_email_address_add: Add email address - label_enable_notifications: Enable notifications - label_disable_notifications: Disable notifications - setting_search_results_per_page: Search results per page - label_blank_value: blank - permission_copy_issues: Copy issues - error_password_expired: Your password has expired or the administrator requires you - to change it. - field_time_entries_visibility: Time logs visibility - setting_password_max_age: Require password change after - label_parent_task_attributes: Parent tasks attributes - label_parent_task_attributes_derived: Calculated from subtasks - label_parent_task_attributes_independent: Independent of subtasks - label_time_entries_visibility_all: All time entries - label_time_entries_visibility_own: Time entries created by the user - label_member_management: Member management - label_member_management_all_roles: All roles - label_member_management_selected_roles_only: Only these roles - label_password_required: Confirm your password to continue - label_total_spent_time: Overall spent time - notice_import_finished: "%{count} items have been imported" - notice_import_finished_with_errors: "%{count} out of %{total} items could not be imported" - error_invalid_file_encoding: The file is not a valid %{encoding} encoded file - error_invalid_csv_file_or_settings: The file is not a CSV file or does not match the - settings below - error_can_not_read_import_file: An error occurred while reading the file to import - permission_import_issues: Import issues - label_import_issues: Import issues - label_select_file_to_import: Select the file to import - label_fields_separator: Field separator - label_fields_wrapper: Field wrapper - label_encoding: Encoding - label_comma_char: Comma - label_semi_colon_char: Semicolon - label_quote_char: Quote - label_double_quote_char: Double quote - label_fields_mapping: Fields mapping - label_file_content_preview: File content preview - label_create_missing_values: Create missing values - button_import: Import - field_total_estimated_hours: Total estimated time + label_email_address_add: Додати email адреси + label_enable_notifications: Увімкнути сповіщення + label_disable_notifications: Вимкнути сповіщення + setting_search_results_per_page: Кількість знайдених результатів на сторінку + label_blank_value: пусто + permission_copy_issues: Копіювання задач + error_password_expired: Термін дії вашого паролю закінчився або адміністратор запросив поміняти його. + field_time_entries_visibility: Видимість трудовитрат + setting_password_max_age: Портребувати заміну пароля по завершенню + label_parent_task_attributes: Атрибути батьківської задачі + label_parent_task_attributes_derived: З урахуванням підзадач + label_parent_task_attributes_independent: Без урахування підзадач + label_time_entries_visibility_all: Всі трудовитрати + label_time_entries_visibility_own: Тільки власні трудовитрати + label_member_management: Управління учасниками + label_member_management_all_roles: Всі ролі + label_member_management_selected_roles_only: Тільки дані ролі + label_password_required: Підтвердіть ваш пароль для продовження + label_total_spent_time: Всього затрачено часу + notice_import_finished: "%{count} елементи(ів) було імпортовано" + notice_import_finished_with_errors: "%{count} з %{total} елементи(ів) неможливо імпортувати" + error_invalid_file_encoding: Кодування файлу не відповідає видраній(ому) %{encoding} + error_invalid_csv_file_or_settings: Файл не є файлом CSV або не відповідає вибраним налаштуванням + error_can_not_read_import_file: Під час читання файлу для імпорту виникла помилка + permission_import_issues: Імпорт задач + label_import_issues: Імпорт задач + label_select_file_to_import: Виберіть файл для імпорту + label_fields_separator: Розділювач + label_fields_wrapper: Обмежувач + label_encoding: Кодування + label_comma_char: Кома + label_semi_colon_char: Крапка з комою + label_quote_char: Дужки + label_double_quote_char: Подвійні дужки + label_fields_mapping: Відповідність полів + label_file_content_preview: Попередній перегляд вмісту файлу + label_create_missing_values: Створити відсутні значення + button_import: Імпорт + field_total_estimated_hours: Всього залишилось часу label_api: API - label_total_plural: Totals - label_assigned_issues: Assigned issues - label_field_format_enumeration: Key/value list - label_f_hour_short: '%{value} h' - field_default_version: Default version - error_attachment_extension_not_allowed: Attachment extension %{extension} is not allowed - setting_attachment_extensions_allowed: Allowed extensions - setting_attachment_extensions_denied: Disallowed extensions - label_any_open_issues: any open issues - label_no_open_issues: no open issues - label_default_values_for_new_users: Default values for new users - error_ldap_bind_credentials: Invalid LDAP Account/Password - setting_sys_api_key: API key + label_total_plural: Висновки + label_assigned_issues: Призначені задачі + label_field_format_enumeration: Ключ/значення список + label_f_hour_short: '%{value} г' + field_default_version: Версія за замовчуванням + error_attachment_extension_not_allowed: Дане розширення %{extension} заборонено + setting_attachment_extensions_allowed: Дозволені розширенні + setting_attachment_extensions_denied: Заборонені розширення + label_any_open_issues: будь-які відкриті задачі + label_no_open_issues: немає відкритих задач + label_default_values_for_new_users: Значення за замовчуванням для нових користувачів + error_ldap_bind_credentials: Неправильний обліковий запис LDAP /Пароль + setting_sys_api_key: API ключ setting_lost_password: Забули пароль - mail_subject_security_notification: Security notification - mail_body_security_notification_change: ! '%{field} was changed.' - mail_body_security_notification_change_to: ! '%{field} was changed to %{value}.' - mail_body_security_notification_add: ! '%{field} %{value} was added.' - mail_body_security_notification_remove: ! '%{field} %{value} was removed.' - mail_body_security_notification_notify_enabled: Email address %{value} now receives - notifications. - mail_body_security_notification_notify_disabled: Email address %{value} no longer - receives notifications. - mail_body_settings_updated: ! 'The following settings were changed:' - field_remote_ip: IP address - label_wiki_page_new: New wiki page - label_relations: Relations - button_filter: Filter - mail_body_password_updated: Your password has been changed. - label_no_preview: No preview available - error_no_tracker_allowed_for_new_issue_in_project: The project doesn't have any trackers - for which you can create an issue - label_tracker_all: All trackers - label_new_project_issue_tab_enabled: Display the "New issue" tab - setting_new_item_menu_tab: Project menu tab for creating new objects - label_new_object_tab_enabled: Display the "+" drop-down - error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers - for which you can create an issue - error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot - be reassigned to an issue that is about to be deleted + mail_subject_security_notification: Сповіщення безпеки + mail_body_security_notification_change: ! '%{field} змінено.' + mail_body_security_notification_change_to: ! '%{field} було змінено на %{value}.' + mail_body_security_notification_add: ! '%{field} %{value} додано.' + mail_body_security_notification_remove: ! '%{field} %{value} видалено.' + mail_body_security_notification_notify_enabled: Email адреса %{value} зараз отримує сповіщення. + mail_body_security_notification_notify_disabled: Email адреса %{value} більше не отримує сповіщення. + mail_body_settings_updated: ! 'Наступні налаштування було змінено:' + field_remote_ip: IP адреси + label_wiki_page_new: Нова wiki сторінка + label_relations: Зв'язки + button_filter: Фільтр + mail_body_password_updated: Ваш пароль змінено. + label_no_preview: Попередній перегляд недоступний + error_no_tracker_allowed_for_new_issue_in_project: Проект не містить трекерів, для яких можна створити задачу + label_tracker_all: Всі трекери + label_new_project_issue_tab_enabled: Відображати вкладку "Нова задача" + setting_new_item_menu_tab: Вкладка "меню проекту" для створення нових об'єктів + label_new_object_tab_enabled: Відображати випадаючий список "+" + error_no_projects_with_tracker_allowed_for_new_issue: Немає проектів з трекерами, для яких можна було б створити задачу + field_textarea_font: Шрифт, який використовується для текстових полів + label_font_default: Шрифт за замовчуванням + label_font_monospace: Моноширинний шрифт + label_font_proportional: Пропорційний шрифт + setting_timespan_format: Формат часового діапазону + label_table_of_contents: Зміст + setting_commit_logs_formatting: Застосувати форматування тексту для повідомлення + setting_mail_handler_enable_regex_delimiters: Використовувати регулярні вирази + error_move_of_child_not_possible: 'Підзадача %{child} не може бути перенесена в новий проект: %{errors}' + error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Витрачений час не можна перенести на задачу, яка має бути видалена + setting_timelog_required_fields: Обов'язкові поля для журналу часу + label_attribute_of_object: '%{object_name}''s %{name}' + warning_fields_cleared_on_bulk_edit: Зміни приведуть до автоматичного видалення значень з одного або декількох полів на обраних об'єктах + field_updated_by: Оновлено + field_last_updated_by: Востаннє оновлено + 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_owner: Only for things I watch or I am the owner of + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/vi.yml b/config/locales/vi.yml index bd3aad6ea..561b80227 100644 --- a/config/locales/vi.yml +++ b/config/locales/vi.yml @@ -145,6 +145,8 @@ vi: circular_dependency: "quan hệ có thể gây ra lặp vô tận" cant_link_an_issue_with_a_descendant: "Một vấn đề không thể liên kết tới một trong số những tác vụ con của nó" earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" + 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" direction: ltr date: @@ -507,7 +509,6 @@ vi: label_internal: Nội bộ label_last_changes: "%{count} thay đổi cuối" label_change_view_all: Xem mọi thay đổi - label_personalize_page: Điều chỉnh trang này label_comment: Bình luận label_comment_plural: Bình luận label_x_comments: @@ -653,7 +654,6 @@ vi: label_age: Thời gian label_change_properties: Thay đổi thuộc tính label_general: Tổng quan - label_more: Chi tiết label_scm: SCM label_plugins: Module label_ldap_authentication: Chứng thực LDAP @@ -663,7 +663,6 @@ vi: label_preferences: Cấu hình label_chronological_order: Bài cũ xếp trước label_reverse_chronological_order: Bài mới xếp trước - label_planning: Kế hoạch label_incoming_emails: Nhận mail label_generate_key: Tạo mã label_issue_watchers: Theo dõi @@ -947,7 +946,6 @@ vi: error_can_not_remove_role: Quyền này đang được dùng và không thể xóa được. error_can_not_delete_tracker: Theo dõi này chứa vấn đề và không thể xóa được. field_principal: Chủ yếu - label_my_page_block: Block trang của tôi notice_failed_to_save_members: "Thất bại khi lưu thành viên : %{errors}." text_zoom_out: Thu nhỏ text_zoom_in: Phóng to @@ -959,10 +957,8 @@ vi: button_edit_associated_wikipage: "Chỉnh sửa trang Wiki liên quan: %{page_title}" text_are_you_sure_with_children: Xóa vấn đề và tất cả vấn đề con? field_text: Trường văn bản - label_user_mail_option_only_owner: Chỉ những thứ tôi sở hữu setting_default_notification_option: Tuỳ chọn thông báo mặc định label_user_mail_option_only_my_events: Chỉ những thứ tôi theo dõi hoặc liên quan - label_user_mail_option_only_assigned: Chỉ những thứ tôi được phân công label_user_mail_option_none: Không có sự kiện field_member_of_group: Nhóm thụ hưởng field_assigned_to_role: Quyền thụ hưởng @@ -1023,16 +1019,12 @@ vi: description_project_scope: Phạm vi tìm kiếm description_filter: Lọc description_user_mail_notification: Thiết lập email thông báo - description_date_from: Nhập ngày bắt đầu description_message_content: Nội dung thông điệp description_available_columns: Các cột có sẵn - description_date_range_interval: Chọn khoảng thời gian giữa ngày bắt đầu và kết thúc description_issue_category_reassign: Chọn danh mục vấn đề description_search: Trường tìm kiếm description_notes: Các chú ý - description_date_range_list: Chọn khoảng từ danh sách description_choose_project: Các dự án - description_date_to: Nhập ngày kết thúc description_query_sort_criteria_attribute: Sắp xếp thuộc tính description_wiki_subpages_reassign: Chọn một trang cấp trên label_parent_revision: Cha @@ -1261,5 +1253,31 @@ vi: label_new_object_tab_enabled: Display the "+" drop-down error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers for which you can create an issue + field_textarea_font: Font used for text areas + label_font_default: Default font + label_font_monospace: Monospaced font + label_font_proportional: Proportional font + setting_timespan_format: Time span format + label_table_of_contents: Table of contents + setting_commit_logs_formatting: Apply text formatting to commit messages + setting_mail_handler_enable_regex_delimiters: Enable regular expressions + error_move_of_child_not_possible: 'Subtask %{child} could not be moved to the new + project: %{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: Spent time cannot be reassigned to an issue that is about to be deleted + setting_timelog_required_fields: Required fields for time logs + 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_owner: Only for things I watch or I am the owner of + warning_fields_cleared_on_bulk_edit: Changes will result in the automatic deletion + of values from one or more fields on the selected objects + field_updated_by: Updated by + field_last_updated_by: Last updated by + field_full_width_layout: Full width layout + label_last_notes: Last notes + field_digest: Checksum + field_default_assigned_to: Default assignee + setting_show_custom_fields_on_registration: Show custom fields on registration + permission_view_news: View news + label_no_preview_alternative_html: No preview available. %{link} the file instead. + label_no_preview_download: Download diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 342d0c398..5f0389fa4 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -187,6 +187,8 @@ circular_dependency: "這個關聯會導致環狀相依" cant_link_an_issue_with_a_descendant: "議題無法被連結至自己的子任務" earlier_than_minimum_start_date: "不能早於 %{date} 因為有前置議題" + 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" # You can define own errors for models or model attributes. # The values :model, :attribute and :value are always available for interpolation. @@ -299,7 +301,9 @@ error_ldap_bind_credentials: "無效的 LDAP 帳號/密碼" error_no_tracker_allowed_for_new_issue_in_project: "此專案沒有您可用來建立新議題的追蹤標籤" error_no_projects_with_tracker_allowed_for_new_issue: "此追蹤標籤沒有您可用來建立新議題的專案" + error_move_of_child_not_possible: "子任務 %{child} 無法被搬移至新的專案: %{errors}" error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: "無法將耗用工時重新分配給即將被刪除的問題" + warning_fields_cleared_on_bulk_edit: "選取物件的變更將導致一個或多個欄位之內容值被自動刪除" mail_subject_lost_password: 您的 Redmine 網站密碼 mail_body_lost_password: '欲變更您的 Redmine 網站密碼, 請點選以下鏈結:' @@ -449,6 +453,12 @@ field_total_estimated_hours: 預估工時總計 field_default_version: 預設版本 field_remote_ip: IP 位址 + field_textarea_font: 文字區域使用的字型 + field_updated_by: 更新者 + field_last_updated_by: 上次更新者 + field_full_width_layout: 全寬度式版面配置 + field_digest: 總和檢查碼 + field_default_assigned_to: 預設被分派者 setting_app_title: 標題 setting_app_subtitle: 副標題 @@ -456,6 +466,7 @@ setting_default_language: 預設語言 setting_login_required: 需要驗證 setting_self_registration: 註冊選項 + setting_show_custom_fields_on_registration: 註冊時顯示自訂欄位 setting_attachment_max_size: 附件大小限制 setting_issues_export_limit: 議題匯出限制 setting_mail_from: 寄件者電子郵件 @@ -473,6 +484,7 @@ setting_autologin: 自動登入 setting_date_format: 日期格式 setting_time_format: 時間格式 + setting_timespan_format: 時間範圍格式 setting_cross_project_issue_relations: 允許關聯至其它專案的議題 setting_cross_project_subtasks: 允許跨專案的子任務 setting_issue_list_default_columns: 預設顯示於議題清單的欄位 @@ -486,6 +498,7 @@ setting_display_subprojects_issues: 預設於父專案中顯示子專案的議題 setting_enabled_scm: 啟用的 SCM setting_mail_handler_body_delimiters: "截去郵件中包含下列值之後的內容" + setting_mail_handler_enable_regex_delimiters: "啟用規則運算式" setting_mail_handler_api_enabled: 啟用處理傳入電子郵件的服務 setting_mail_handler_api_key: 傳入電子郵件網頁服務 API 金鑰 setting_sys_api_key: 儲存機制管理網頁服務 API 金鑰 @@ -531,6 +544,8 @@ setting_attachment_extensions_allowed: 允許使用的附檔名 setting_attachment_extensions_denied: 禁止使用的副檔名 setting_new_item_menu_tab: 建立新物件的專案功能分頁 + setting_commit_logs_formatting: 套用文字格式至認可訊息 + setting_timelog_required_fields: 工時記錄必填欄位 permission_add_project: 建立專案 permission_add_subprojects: 建立子專案 @@ -566,6 +581,7 @@ permission_view_time_entries: 檢視耗用工時 permission_edit_time_entries: 編輯工時紀錄 permission_edit_own_time_entries: 編輯自己的工時記錄 + permission_view_news: 檢視新聞 permission_manage_news: 管理新聞 permission_comment_news: 回應新聞 permission_view_documents: 檢視文件 @@ -673,7 +689,6 @@ label_my_page: 帳戶首頁 label_my_account: 我的帳戶 label_my_projects: 我的專案 - label_my_page_block: 帳戶首頁區塊 label_administration: 網站管理 label_login: 登入 label_logout: 登出 @@ -707,7 +722,9 @@ label_attribute: 屬性 label_attribute_plural: 屬性 label_no_data: 沒有任何資料可供顯示 - label_no_preview: 沒有任何預覽可供使用 + label_no_preview: 無法預覽 + label_no_preview_alternative_html: 無法預覽. 請改為使用 %{link} 此檔案. + label_no_preview_download: 下載 label_change_status: 變更狀態 label_history: 歷史 label_attachment: 檔案 @@ -771,7 +788,6 @@ label_internal: 內部 label_last_changes: "最近 %{count} 個變更" label_change_view_all: 檢視全部的變更 - label_personalize_page: 自訂版面 label_comment: 回應 label_comment_plural: 回應 label_x_comments: @@ -941,8 +957,8 @@ label_user_mail_option_selected: "只提醒我所選擇專案中的事件..." label_user_mail_option_none: "取消提醒" label_user_mail_option_only_my_events: "只提醒我觀察中或參與中的事物" - label_user_mail_option_only_assigned: "只提醒我被分派的事物" - label_user_mail_option_only_owner: "只提醒我作為擁有者的事物" + label_user_mail_option_only_assigned: "只提醒我觀察中或分派給我的事物" + label_user_mail_option_only_owner: "只提醒我觀察中或擁有者為我的事物" label_user_mail_no_self_notified: "不提醒我自己所做的變更" label_registration_activation_by_email: 透過電子郵件啟用帳戶 label_registration_manual_activation: 手動啟用帳戶 @@ -951,7 +967,6 @@ label_age: 年齡 label_change_properties: 變更屬性 label_general: 一般 - label_more: 更多 » label_scm: 版本控管 label_plugins: 外掛程式 label_ldap_authentication: LDAP 認證 @@ -961,7 +976,6 @@ label_preferences: 偏好選項 label_chronological_order: 以時間由遠至近排序 label_reverse_chronological_order: 以時間由近至遠排序 - label_planning: 計劃表 label_incoming_emails: 傳入的電子郵件 label_generate_key: 產生金鑰 label_issue_watchers: 監看者 @@ -1018,12 +1032,13 @@ label_readonly: 唯讀 label_required: 必填 label_hidden: 隱藏 - label_attribute_of_project: "專案是 %{name}" - label_attribute_of_issue: "議題是 %{name}" - label_attribute_of_author: "作者是 %{name}" - label_attribute_of_assigned_to: "被分派者是 %{name}" - label_attribute_of_user: "用戶是 %{name}" - label_attribute_of_fixed_version: "版本是 %{name}" + label_attribute_of_project: "專案的 %{name}" + label_attribute_of_issue: "議題的 %{name}" + label_attribute_of_author: "作者的 %{name}" + label_attribute_of_assigned_to: "被分派者的 %{name}" + label_attribute_of_user: "用戶的 %{name}" + label_attribute_of_fixed_version: "版本的 %{name}" + label_attribute_of_object: "%{object_name}的 %{name}" label_cross_project_descendants: 與子專案共用 label_cross_project_tree: 與專案樹共用 label_cross_project_hierarchy: 與專案階層架構共用 @@ -1083,6 +1098,11 @@ label_relations: 關聯 label_new_project_issue_tab_enabled: 顯示「建立新議題」標籤頁面 label_new_object_tab_enabled: 顯示 "+" 下拉功能表 + label_table_of_contents: 目錄 + label_font_default: 預設字型 + label_font_monospace: 等寬字型 + label_font_proportional: 調和間距字型 + label_last_notes: 最後一則筆記 button_login: 登入 button_submit: 送出 @@ -1274,8 +1294,4 @@ description_all_columns: 所有欄位 description_issue_category_reassign: 選擇議題分類 description_wiki_subpages_reassign: 選擇新的父頁面 - description_date_range_list: 從清單中選取範圍 - description_date_range_interval: 選擇起始與結束日期以設定範圍區間 - description_date_from: 輸入起始日期 - description_date_to: 輸入結束日期 text_repository_identifier_info: '僅允許使用小寫英文字母 (a-z), 阿拉伯數字, 虛線與底線。
    一旦儲存之後, 代碼便無法再次被更改。' diff --git a/config/locales/zh.yml b/config/locales/zh.yml index 5a846b679..d0624f5f5 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -134,6 +134,8 @@ zh: circular_dependency: "此关联将导致循环依赖" cant_link_an_issue_with_a_descendant: "问题不能关联到它的子任务" earlier_than_minimum_start_date: "不能早于 %{date} 由于有前置问题" + not_a_regexp: "不是一个合法的正则表达式" + open_issue_with_closed_parent: "无法将一个打开的问题关联至一个被关闭的父任务" actionview_instancetag_blank_option: 请选择 @@ -492,7 +494,6 @@ zh: label_my_page: 我的工作台 label_my_account: 我的帐号 label_my_projects: 我的项目 - label_my_page_block: 我的工作台模块 label_administration: 管理 label_login: 登录 label_logout: 退出 @@ -580,7 +581,6 @@ zh: label_internal: 内部 label_last_changes: "最近的 %{count} 次变更" label_change_view_all: 查看所有变更 - label_personalize_page: 个性化定制本页 label_comment: 评论 label_comment_plural: 评论 label_x_comments: @@ -732,8 +732,6 @@ zh: label_user_mail_option_selected: "收取选中项目的所有通知..." label_user_mail_option_none: "不收取任何通知" label_user_mail_option_only_my_events: "只收取我跟踪或参与的项目的通知" - label_user_mail_option_only_assigned: "只收取指派给我的" - label_user_mail_option_only_owner: 只收取由我创建的 label_user_mail_no_self_notified: "不要发送对我自己提交的修改的通知" label_registration_activation_by_email: 通过邮件认证激活帐号 label_registration_manual_activation: 手动激活帐号 @@ -742,7 +740,6 @@ zh: label_age: 提交时间 label_change_properties: 修改属性 label_general: 一般 - label_more: 更多 label_scm: SCM label_plugins: 插件 label_ldap_authentication: LDAP 认证 @@ -752,7 +749,6 @@ zh: label_preferences: 首选项 label_chronological_order: 按时间顺序 label_reverse_chronological_order: 按时间顺序(倒序) - label_planning: 计划 label_incoming_emails: 接收邮件 label_generate_key: 生成一个key label_issue_watchers: 跟踪者 @@ -974,16 +970,12 @@ zh: description_project_scope: 搜索范围 description_filter: 过滤器 description_user_mail_notification: 邮件通知设置 - description_date_from: 输入开始日期 description_message_content: 信息内容 description_available_columns: 备选列 - description_date_range_interval: 按开始日期和结束日期选择范围 description_issue_category_reassign: 选择问题类别 description_search: 搜索字段 description_notes: 批注 - description_date_range_list: 从列表中选择范围 description_choose_project: 项目 - description_date_to: 输入结束日期 description_query_sort_criteria_attribute: 排序方式 description_wiki_subpages_reassign: 选择父页面 description_selected_columns: 已选列 @@ -1200,4 +1192,28 @@ zh: setting_new_item_menu_tab: 建立新对象条目的项目菜单栏目 label_new_object_tab_enabled: 显示 "+" 为下拉列表 error_no_projects_with_tracker_allowed_for_new_issue: 当前项目中不包含对应的跟踪类型,不能创建该类型的工作项。 + field_textarea_font: 用于文本区域的字体 + label_font_default: 默认字体 + label_font_monospace: 等宽字体 + label_font_proportional: 比例字体 + setting_timespan_format: 时间格式设置 + label_table_of_contents: 目录 + setting_commit_logs_formatting: 在提交日志消息时,应用文本格式 + setting_mail_handler_enable_regex_delimiters: 启用正则表达式 + error_move_of_child_not_possible: '子任务 %{child} 不能移动到新项目:%{errors}' error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: 已用耗时不能重新分配到即将被删除的任务里。 + setting_timelog_required_fields: 工时登记必填字段 + label_attribute_of_object: '%{object_name} 的 %{name} 属性' + label_user_mail_option_only_assigned: 只发送我关注或指派给我的相关信息 + label_user_mail_option_only_owner: 只发送我关注或我创建的相关信息 + warning_fields_cleared_on_bulk_edit: 修改将导致所选内容的一个或多个字段值被自动删除。 + field_updated_by: 更新人 + field_last_updated_by: 最近更新人 + field_full_width_layout: 全宽布局 + label_last_notes: 最近批注 + field_digest: 校验和 + field_default_assigned_to: 默认指派给 + setting_show_custom_fields_on_registration: 注册时显示自定义字段 + permission_view_news: 查看新闻 + label_no_preview_alternative_html: 无法预览。请使用文件 %{link} 查阅。 + label_no_preview_download: 下载 diff --git a/config/routes.rb b/config/routes.rb index 06b500120..d28b6afa1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -74,12 +74,12 @@ Rails.application.routes.draw do match 'my/account', :controller => 'my', :action => 'account', :via => [:get, :post] match 'my/account/destroy', :controller => 'my', :action => 'destroy', :via => [:get, :post] match 'my/page', :controller => 'my', :action => 'page', :via => :get + post 'my/page', :to => 'my#update_page' match 'my', :controller => 'my', :action => 'index', :via => :get # Redirects to my/page get 'my/api_key', :to => 'my#show_api_key', :as => 'my_api_key' post 'my/api_key', :to => 'my#reset_api_key' post 'my/rss_key', :to => 'my#reset_rss_key', :as => 'my_rss_key' match 'my/password', :controller => 'my', :action => 'password', :via => [:get, :post] - match 'my/page_layout', :controller => 'my', :action => 'page_layout', :via => :get match 'my/add_block', :controller => 'my', :action => 'add_block', :via => :post match 'my/remove_block', :controller => 'my', :action => 'remove_block', :via => :post match 'my/order_blocks', :controller => 'my', :action => 'order_blocks', :via => :post @@ -101,6 +101,10 @@ Rails.application.routes.draw do delete 'issues/:object_id/watchers/:user_id' => 'watchers#destroy', :object_type => 'issue' resources :projects do + collection do + get 'autocomplete' + end + member do get 'settings(/:tab)', :action => 'settings', :as => 'settings' post 'modules' @@ -112,7 +116,7 @@ Rails.application.routes.draw do end shallow do - resources :memberships, :controller => 'members', :only => [:index, :show, :new, :create, :update, :destroy] do + resources :memberships, :controller => 'members' do collection do get 'autocomplete' end @@ -188,11 +192,7 @@ Rails.application.routes.draw do match 'bulk_edit', :via => [:get, :post] post 'bulk_update' end - resources :time_entries, :controller => 'timelog', :except => [:show, :edit, :update, :destroy] do - collection do - get 'report' - end - end + resources :time_entries, :controller => 'timelog', :only => [:new, :create] shallow do resources :relations, :controller => 'issue_relations', :only => [:index, :show, :create, :destroy] end @@ -202,6 +202,7 @@ Rails.application.routes.draw do match '/issues', :controller => 'issues', :action => 'destroy', :via => :delete resources :queries, :except => [:show] + get '/queries/filter', :to => 'queries#filter', :as => 'queries_filter' resources :news, :only => [:index, :show, :edit, :update, :destroy] match '/news/:id/comments', :to => 'comments#create', :via => :post @@ -218,6 +219,10 @@ Rails.application.routes.draw do match '/time_entries/context_menu', :to => 'context_menus#time_entries', :as => :time_entries_context_menu, :via => [:get, :post] resources :time_entries, :controller => 'timelog', :except => :destroy do + member do + # Used when updating the edit form of an existing time entry + patch 'edit', :to => 'timelog#edit' + end collection do get 'report' get 'bulk_edit' @@ -246,13 +251,13 @@ Rails.application.routes.draw do post 'projects/:id/repository/:repository_id/revisions/:rev/issues', :to => 'repositories#add_related_issue' delete 'projects/:id/repository/:repository_id/revisions/:rev/issues/:issue_id', :to => 'repositories#remove_related_issue' get 'projects/:id/repository/:repository_id/revisions', :to => 'repositories#revisions' - get 'projects/:id/repository/:repository_id/revisions/:rev/:action(/*path)', - :controller => 'repositories', - :format => false, - :constraints => { - :action => /(browse|show|entry|raw|annotate|diff)/, - :rev => /[a-z0-9\.\-_]+/ - } + %w(browse show entry raw annotate diff).each do |action| + get "projects/:id/repository/:repository_id/revisions/:rev/#{action}(/*path)", + :controller => 'repositories', + :action => action, + :format => false, + :constraints => {:rev => /[a-z0-9\.\-_]+/} + end get 'projects/:id/repository/statistics', :to => 'repositories#stats' get 'projects/:id/repository/graph', :to => 'repositories#graph' @@ -266,21 +271,28 @@ Rails.application.routes.draw do get 'projects/:id/repository/revision', :to => 'repositories#revision' post 'projects/:id/repository/revisions/:rev/issues', :to => 'repositories#add_related_issue' delete 'projects/:id/repository/revisions/:rev/issues/:issue_id', :to => 'repositories#remove_related_issue' - get 'projects/:id/repository/revisions/:rev/:action(/*path)', - :controller => 'repositories', - :format => false, - :constraints => { - :action => /(browse|show|entry|raw|annotate|diff)/, - :rev => /[a-z0-9\.\-_]+/ - } - get 'projects/:id/repository/:repository_id/:action(/*path)', - :controller => 'repositories', - :action => /(browse|show|entry|raw|changes|annotate|diff)/, - :format => false - get 'projects/:id/repository/:action(/*path)', - :controller => 'repositories', - :action => /(browse|show|entry|raw|changes|annotate|diff)/, - :format => false + %w(browse show entry raw annotate diff).each do |action| + get "projects/:id/repository/revisions/:rev/#{action}(/*path)", + :controller => 'repositories', + :action => action, + :format => false, + :constraints => {:rev => /[a-z0-9\.\-_]+/} + end + %w(browse entry raw changes annotate diff).each do |action| + get "projects/:id/repository/:repository_id/#{action}(/*path)", + :controller => 'repositories', + :action => action, + :format => false + end + %w(browse entry raw changes annotate diff).each do |action| + get "projects/:id/repository/#{action}(/*path)", + :controller => 'repositories', + :action => action, + :format => false + end + + get 'projects/:id/repository/:repository_id/show/*path', :to => 'repositories#show', :format => false + get 'projects/:id/repository/show/*path', :to => 'repositories#show', :format => false get 'projects/:id/repository/:repository_id', :to => 'repositories#show', :path => nil get 'projects/:id/repository', :to => 'repositories#show', :path => nil @@ -290,9 +302,9 @@ Rails.application.routes.draw do get 'attachments/download/:id/:filename', :to => 'attachments#download', :id => /\d+/, :filename => /.*/, :as => 'download_named_attachment' get 'attachments/download/:id', :to => 'attachments#download', :id => /\d+/ get 'attachments/thumbnail/:id(/:size)', :to => 'attachments#thumbnail', :id => /\d+/, :size => /\d+/, :as => 'thumbnail' - resources :attachments, :only => [:show, :destroy] - get 'attachments/:object_type/:object_id/edit', :to => 'attachments#edit', :as => :object_attachments_edit - patch 'attachments/:object_type/:object_id', :to => 'attachments#update', :as => :object_attachments + resources :attachments, :only => [:show, :update, :destroy] + get 'attachments/:object_type/:object_id/edit', :to => 'attachments#edit_all', :as => :object_attachments_edit + patch 'attachments/:object_type/:object_id', :to => 'attachments#update_all', :as => :object_attachments resources :groups do resources :memberships, :controller => 'principal_memberships' @@ -366,7 +378,7 @@ Rails.application.routes.draw do get 'robots.txt', :to => 'welcome#robots' - Dir.glob File.expand_path("plugins/*", Rails.root) do |plugin_dir| + Dir.glob File.expand_path("#{Redmine::Plugin.directory}/*") do |plugin_dir| file = File.join(plugin_dir, "config/routes.rb") if File.exists?(file) begin diff --git a/config/settings.yml b/config/settings.yml index 807f9b7a7..a64697755 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -31,6 +31,8 @@ login_required: self_registration: default: '2' security_notifications: 1 +show_custom_fields_on_registration: + default: 1 lost_password: default: 1 security_notifications: 1 @@ -153,6 +155,8 @@ date_format: default: '' time_format: default: '' +timespan_format: + default: 'decimal' user_format: default: :firstname_lastname format: symbol @@ -180,6 +184,8 @@ notified_events: - issue_updated mail_handler_body_delimiters: default: '' +mail_handler_enable_regex_delimiters: + default: 0 mail_handler_excluded_filenames: default: '' mail_handler_api_enabled: @@ -232,11 +238,15 @@ sequential_project_identifiers: # multiple values accepted, comma separated default_users_hide_mail: default: 1 +default_users_time_zone: + default: "" repositories_encodings: default: '' # encoding used to convert commit logs to UTF-8 commit_logs_encoding: default: 'UTF-8' +commit_logs_formatting: + default: 1 repository_log_display_limit: format: int default: 100 @@ -277,3 +287,6 @@ non_working_week_days: - '7' new_item_menu_tab: default: 2 +timelog_required_fields: + serialized: true + default: [] diff --git a/db/migrate/062_insert_builtin_roles.rb b/db/migrate/062_insert_builtin_roles.rb index 070d9285f..ae3a706e2 100644 --- a/db/migrate/062_insert_builtin_roles.rb +++ b/db/migrate/062_insert_builtin_roles.rb @@ -11,6 +11,6 @@ class InsertBuiltinRoles < ActiveRecord::Migration end def self.down - Role.destroy_all 'builtin <> 0' + Role.where('builtin <> 0').destroy_all end end diff --git a/db/migrate/072_add_enumerations_position.rb b/db/migrate/072_add_enumerations_position.rb index cf5646cf9..2834abef1 100644 --- a/db/migrate/072_add_enumerations_position.rb +++ b/db/migrate/072_add_enumerations_position.rb @@ -4,7 +4,7 @@ class AddEnumerationsPosition < ActiveRecord::Migration Enumeration.all.group_by(&:opt).each do |opt, enums| enums.each_with_index do |enum, i| # do not call model callbacks - Enumeration.where({:id => enum.id}).update_all("position = #{i+1}") + Enumeration.where({:id => enum.id}).update_all(:position => (i+1)) end end end diff --git a/db/migrate/078_add_custom_fields_position.rb b/db/migrate/078_add_custom_fields_position.rb index ef940eb02..a03db4618 100644 --- a/db/migrate/078_add_custom_fields_position.rb +++ b/db/migrate/078_add_custom_fields_position.rb @@ -4,7 +4,7 @@ class AddCustomFieldsPosition < ActiveRecord::Migration CustomField.all.group_by(&:type).each do |t, fields| fields.each_with_index do |field, i| # do not call model callbacks - CustomField.where({:id => field.id}).update_all("position = #{i+1}") + CustomField.where({:id => field.id}).update_all(:position => (i+1)) end end end diff --git a/db/migrate/098_set_topic_authors_as_watchers.rb b/db/migrate/098_set_topic_authors_as_watchers.rb index 92a53f4a1..1a1529561 100644 --- a/db/migrate/098_set_topic_authors_as_watchers.rb +++ b/db/migrate/098_set_topic_authors_as_watchers.rb @@ -10,6 +10,6 @@ class SetTopicAuthorsAsWatchers < ActiveRecord::Migration def self.down # Removes all message watchers - Watcher.delete_all("watchable_type = 'Message'") + Watcher.where("watchable_type = 'Message'").delete_all end end diff --git a/db/migrate/20100819172912_enable_calendar_and_gantt_modules_where_appropriate.rb b/db/migrate/20100819172912_enable_calendar_and_gantt_modules_where_appropriate.rb index 5bee50e3a..6071adf52 100644 --- a/db/migrate/20100819172912_enable_calendar_and_gantt_modules_where_appropriate.rb +++ b/db/migrate/20100819172912_enable_calendar_and_gantt_modules_where_appropriate.rb @@ -7,6 +7,6 @@ class EnableCalendarAndGanttModulesWhereAppropriate < ActiveRecord::Migration end def self.down - EnabledModule.delete_all("name = 'calendar' OR name = 'gantt'") + EnabledModule.where("name = 'calendar' OR name = 'gantt'").delete_all end end diff --git a/db/migrate/20101104182107_add_unique_index_on_members.rb b/db/migrate/20101104182107_add_unique_index_on_members.rb index 14d1585f7..eabdad86b 100644 --- a/db/migrate/20101104182107_add_unique_index_on_members.rb +++ b/db/migrate/20101104182107_add_unique_index_on_members.rb @@ -1,7 +1,7 @@ class AddUniqueIndexOnMembers < ActiveRecord::Migration def self.up # Clean and reassign MemberRole rows if needed - MemberRole.delete_all("member_id NOT IN (SELECT id FROM #{Member.table_name})") + MemberRole.where("member_id NOT IN (SELECT id FROM #{Member.table_name})").delete_all MemberRole.update_all("member_id =" + " (SELECT min(m2.id) FROM #{Member.table_name} m1, #{Member.table_name} m2" + " WHERE m1.user_id = m2.user_id AND m1.project_id = m2.project_id" + @@ -9,7 +9,7 @@ class AddUniqueIndexOnMembers < ActiveRecord::Migration # Remove duplicates Member.connection.select_values("SELECT m.id FROM #{Member.table_name} m" + " WHERE m.id > (SELECT min(m1.id) FROM #{Member.table_name} m1 WHERE m1.user_id = m.user_id AND m1.project_id = m.project_id)").each do |i| - Member.delete_all(["id = ?", i]) + Member.where(["id = ?", i]).delete_all end # Then add a unique index diff --git a/db/migrate/20111201201315_add_unique_index_to_issue_relations.rb b/db/migrate/20111201201315_add_unique_index_to_issue_relations.rb index c27158c13..1bee49ab0 100644 --- a/db/migrate/20111201201315_add_unique_index_to_issue_relations.rb +++ b/db/migrate/20111201201315_add_unique_index_to_issue_relations.rb @@ -4,7 +4,7 @@ class AddUniqueIndexToIssueRelations < ActiveRecord::Migration # Remove duplicates IssueRelation.connection.select_values("SELECT r.id FROM #{IssueRelation.table_name} r" + " WHERE r.id > (SELECT min(r1.id) FROM #{IssueRelation.table_name} r1 WHERE r1.issue_from_id = r.issue_from_id AND r1.issue_to_id = r.issue_to_id)").each do |i| - IssueRelation.delete_all(["id = ?", i]) + IssueRelation.where(["id = ?", i]).delete_all end add_index :issue_relations, [:issue_from_id, :issue_to_id], :unique => true diff --git a/db/migrate/20141029181824_remove_issue_statuses_is_default.rb b/db/migrate/20141029181824_remove_issue_statuses_is_default.rb index 62785d37a..c5c813d62 100644 --- a/db/migrate/20141029181824_remove_issue_statuses_is_default.rb +++ b/db/migrate/20141029181824_remove_issue_statuses_is_default.rb @@ -6,7 +6,7 @@ class RemoveIssueStatusesIsDefault < ActiveRecord::Migration def down add_column :issue_statuses, :is_default, :boolean, :null => false, :default => false # Restores the first status as default - default_status_id = IssueStatus.order("position").pluck(:id).first + default_status_id = IssueStatus.order(:position).pluck(:id).first IssueStatus.where(:id => default_status_id).update_all(:is_default => true) end end diff --git a/db/migrate/20150525103953_clear_estimated_hours_on_parent_issues.rb b/db/migrate/20150525103953_clear_estimated_hours_on_parent_issues.rb index 8eed815f7..c00ada096 100644 --- a/db/migrate/20150525103953_clear_estimated_hours_on_parent_issues.rb +++ b/db/migrate/20150525103953_clear_estimated_hours_on_parent_issues.rb @@ -6,7 +6,7 @@ class ClearEstimatedHoursOnParentIssues < ActiveRecord::Migration def self.down table_name = Issue.table_name - leaves_sum_select = "SELECT SUM(leaves.estimated_hours) FROM #{table_name} leaves" + + leaves_sum_select = "SELECT SUM(leaves.estimated_hours) FROM (SELECT * FROM #{table_name}) AS leaves" + " WHERE leaves.root_id = #{table_name}.root_id AND leaves.lft > #{table_name}.lft AND leaves.rgt < #{table_name}.rgt" + " AND leaves.rgt = leaves.lft + 1" diff --git a/db/migrate/20160529063352_add_roles_settings.rb b/db/migrate/20160529063352_add_roles_settings.rb index 921187ec1..a4d18fcbc 100644 --- a/db/migrate/20160529063352_add_roles_settings.rb +++ b/db/migrate/20160529063352_add_roles_settings.rb @@ -2,4 +2,4 @@ class AddRolesSettings < ActiveRecord::Migration def change add_column :roles, :settings, :text end -end \ No newline at end of file +end diff --git a/db/migrate/20161001122012_add_tracker_id_index_to_workflows.rb b/db/migrate/20161001122012_add_tracker_id_index_to_workflows.rb new file mode 100644 index 000000000..10c4dd97b --- /dev/null +++ b/db/migrate/20161001122012_add_tracker_id_index_to_workflows.rb @@ -0,0 +1,9 @@ +class AddTrackerIdIndexToWorkflows < ActiveRecord::Migration + def self.up + add_index :workflows, :tracker_id + end + + def self.down + remove_index :workflows, :tracker_id + end +end diff --git a/db/migrate/20161002133421_add_index_on_member_roles_inherited_from.rb b/db/migrate/20161002133421_add_index_on_member_roles_inherited_from.rb new file mode 100644 index 000000000..2a06a9e6c --- /dev/null +++ b/db/migrate/20161002133421_add_index_on_member_roles_inherited_from.rb @@ -0,0 +1,5 @@ +class AddIndexOnMemberRolesInheritedFrom < ActiveRecord::Migration + def change + add_index :member_roles, :inherited_from + end +end diff --git a/db/migrate/20161010081301_change_issues_description_limit.rb b/db/migrate/20161010081301_change_issues_description_limit.rb new file mode 100644 index 000000000..0f20466a1 --- /dev/null +++ b/db/migrate/20161010081301_change_issues_description_limit.rb @@ -0,0 +1,12 @@ +class ChangeIssuesDescriptionLimit < ActiveRecord::Migration + def up + if ActiveRecord::Base.connection.adapter_name =~ /mysql/i + max_size = 16.megabytes + change_column :issues, :description, :text, :limit => max_size + end + end + + def down + # no-op + end +end diff --git a/db/migrate/20161010081528_change_journal_details_value_limit.rb b/db/migrate/20161010081528_change_journal_details_value_limit.rb new file mode 100644 index 000000000..2314dd535 --- /dev/null +++ b/db/migrate/20161010081528_change_journal_details_value_limit.rb @@ -0,0 +1,13 @@ +class ChangeJournalDetailsValueLimit < ActiveRecord::Migration + def up + if ActiveRecord::Base.connection.adapter_name =~ /mysql/i + max_size = 16.megabytes + change_column :journal_details, :value, :text, :limit => max_size + change_column :journal_details, :old_value, :text, :limit => max_size + end + end + + def down + # no-op + end +end diff --git a/db/migrate/20161010081600_change_journals_notes_limit.rb b/db/migrate/20161010081600_change_journals_notes_limit.rb new file mode 100644 index 000000000..8a2ba9be6 --- /dev/null +++ b/db/migrate/20161010081600_change_journals_notes_limit.rb @@ -0,0 +1,12 @@ +class ChangeJournalsNotesLimit < ActiveRecord::Migration + def up + if ActiveRecord::Base.connection.adapter_name =~ /mysql/i + max_size = 16.megabytes + change_column :journals, :notes, :text, :limit => max_size + end + end + + def down + # no-op + end +end diff --git a/db/migrate/20161126094932_add_index_on_changesets_issues_issue_id.rb b/db/migrate/20161126094932_add_index_on_changesets_issues_issue_id.rb new file mode 100644 index 000000000..dae77256c --- /dev/null +++ b/db/migrate/20161126094932_add_index_on_changesets_issues_issue_id.rb @@ -0,0 +1,5 @@ +class AddIndexOnChangesetsIssuesIssueId < ActiveRecord::Migration + def change + add_index :changesets_issues, :issue_id + end +end diff --git a/db/migrate/20161220091118_add_index_on_issues_parent_id.rb b/db/migrate/20161220091118_add_index_on_issues_parent_id.rb new file mode 100644 index 000000000..1cc94b098 --- /dev/null +++ b/db/migrate/20161220091118_add_index_on_issues_parent_id.rb @@ -0,0 +1,5 @@ +class AddIndexOnIssuesParentId < ActiveRecord::Migration + def change + add_index :issues, :parent_id + end +end diff --git a/db/migrate/20170207050700_add_index_on_disk_filename_to_attachments.rb b/db/migrate/20170207050700_add_index_on_disk_filename_to_attachments.rb new file mode 100644 index 000000000..6f41a9c4b --- /dev/null +++ b/db/migrate/20170207050700_add_index_on_disk_filename_to_attachments.rb @@ -0,0 +1,5 @@ +class AddIndexOnDiskFilenameToAttachments < ActiveRecord::Migration + def change + add_index :attachments, :disk_filename + end +end diff --git a/db/migrate/20170302015225_change_attachments_digest_limit_to_64.rb b/db/migrate/20170302015225_change_attachments_digest_limit_to_64.rb new file mode 100644 index 000000000..df710e82c --- /dev/null +++ b/db/migrate/20170302015225_change_attachments_digest_limit_to_64.rb @@ -0,0 +1,8 @@ +class ChangeAttachmentsDigestLimitTo64 < ActiveRecord::Migration + def up + change_column :attachments, :digest, :string, limit: 64 + end + def down + change_column :attachments, :digest, :string, limit: 40 + end +end diff --git a/db/migrate/20170309214320_add_project_default_assigned_to_id.rb b/db/migrate/20170309214320_add_project_default_assigned_to_id.rb new file mode 100644 index 000000000..97a4b1905 --- /dev/null +++ b/db/migrate/20170309214320_add_project_default_assigned_to_id.rb @@ -0,0 +1,13 @@ +class AddProjectDefaultAssignedToId < ActiveRecord::Migration + def up + add_column :projects, :default_assigned_to_id, :integer, :default => nil + # Try to copy existing settings from the plugin if redmine_default_assign plugin was used + if column_exists?(:projects, :default_assignee_id, :integer) + Project.update_all('default_assigned_to_id = default_assignee_id') + end + end + + def down + remove_column :projects, :default_assigned_to_id + end +end diff --git a/db/migrate/20170320051650_change_repositories_extra_info_limit.rb b/db/migrate/20170320051650_change_repositories_extra_info_limit.rb new file mode 100644 index 000000000..3b5654a6d --- /dev/null +++ b/db/migrate/20170320051650_change_repositories_extra_info_limit.rb @@ -0,0 +1,12 @@ +class ChangeRepositoriesExtraInfoLimit < ActiveRecord::Migration + def up + if ActiveRecord::Base.connection.adapter_name =~ /mysql/i + max_size = 16.megabytes + change_column :repositories, :extra_info, :text, :limit => max_size + end + end + + def down + # no-op + end +end diff --git a/db/migrate/20170418090031_add_view_news_to_all_existing_roles.rb b/db/migrate/20170418090031_add_view_news_to_all_existing_roles.rb new file mode 100644 index 000000000..6f851a1f1 --- /dev/null +++ b/db/migrate/20170418090031_add_view_news_to_all_existing_roles.rb @@ -0,0 +1,9 @@ +class AddViewNewsToAllExistingRoles < ActiveRecord::Migration + def up + Role.all.each { |role| role.add_permission! :view_news } + end + + def down + # nothing to revert + end +end diff --git a/db/migrate/20170419144536_add_view_messages_to_all_existing_roles.rb b/db/migrate/20170419144536_add_view_messages_to_all_existing_roles.rb new file mode 100644 index 000000000..d010ba497 --- /dev/null +++ b/db/migrate/20170419144536_add_view_messages_to_all_existing_roles.rb @@ -0,0 +1,9 @@ +class AddViewMessagesToAllExistingRoles < ActiveRecord::Migration + def up + Role.all.each { |role| role.add_permission! :view_messages } + end + + def down + # nothing to revert + end +end diff --git a/doc/CHANGELOG b/doc/CHANGELOG index ca15b4887..bc287699d 100644 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -4,17 +4,33 @@ Redmine - project management software Copyright (C) 2006-2017 Jean-Philippe Lang http://www.redmine.org/ -== 2018-12-09 v3.3.9 +== 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 @@ -32,9 +48,16 @@ http://www.redmine.org/ * 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] @@ -44,18 +67,39 @@ http://www.redmine.org/ * 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 -== 2018-06-10 v3.3.8 +=== [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 @@ -72,7 +116,15 @@ http://www.redmine.org/ * Defect #22023: Issue id input should get focus after adding related issue -== 2018-04-07 v3.3.7 +=== [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] @@ -91,6 +143,14 @@ http://www.redmine.org/ * 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 @@ -101,21 +161,34 @@ http://www.redmine.org/ * 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.3.6 +== 2018-01-08 v3.4.4 + +=== [Accounts / authentication] + +* Defect #22532: Strip whitespace from login on login page +* Defect #27754: Strip whitespace from email addresses on lost password page === [Administration] @@ -123,8 +196,13 @@ http://www.redmine.org/ === [Calendar] +* Defect #27153: Custom query breaks calendar view with error 500 * Patch #27139: Fix for project link background in calendar tooltips +=== [Custom fields] + +* Defect #26705: Unable to download file if custom field is not defined as visible to any users + === [Email receiving] * Patch #27885: Empty email attachments are imported to Redmine, creating broken DB records @@ -136,9 +214,11 @@ http://www.redmine.org/ === [Gems support] * Defect #27206: cannot install public_suffix if ruby < 2.1 +* Defect #27505: Cannot install nokogiri 1.7 on Windows Ruby 2.4 === [Issues] +* Defect #26880: Cannot clear all watchers when copying an issue * Defect #27110: Changing the tracker to a tracker with the tracker field set to read-only won't work * Defect #27881: No validation errors when entering an invalid "Estimate hours" value * Patch #27663: Same relates relation can be created twice @@ -148,6 +228,10 @@ http://www.redmine.org/ * Defect #27533: Cannot change the priority of the parent issue in issue query context menu when parent priority is independent of children +=== [Plugin API] + +* Defect #20513: Unloadable plugin convention breaks with Rails 4.2.3 + === [SCM] * Defect #27333: Switching SCM fails after validation error in "New repository" page @@ -158,34 +242,68 @@ http://www.redmine.org/ === [Translations] +* Patch #27502: Lithuanian translation for 3.4-stable +* Patch #27620: Brazilian translation update * Patch #27642: Spanish translation update (jstoolbar-es.js) * Patch #27649: Spanish/Panama translation update (jstoolbar-es-pa.js) -* Patch #27765: Lithuanian translation for 3.3-stable -* Patch #27766: Czech translation for 3.3-stable -* Patch #27835: Brazilian translation for 3.3-stable +* Patch #27767: Czech translation for 3.4-stable === [UI] * Defect #19578: Issues reports table header overlaping * Defect #26699: Anonymous user should have their icon -== 2017-10-15 v3.3.5 +== 2017-10-15 v3.4.3 + +=== [Administration] + +* Defect #26564: Enumerations sorting does not work + +=== [Custom fields] + +* Defect #26468: Using custom fields of type "File" leads to unsolvable error if filetype is not allowed + +=== [Issues] + +* Defect #26627: Editing issues no longer sends notifications to previous assignee + +=== [Issues list] + +* Defect #26471: Issue Query: inconsistency between spent_hours sum and sum of shown spent_hours values === [PDF export] * Defect #25702: Exporting wiki page with specific table to PDF causes 500 +=== [Roadmap] + +* Patch #26492: % is not valid without a format specifier + === [SCM] +* Defect #26403: The second and subsequent lines of commit messages are not displayed in repository browser * Defect #26645: git 2.14 compatibility +=== [Text formatting] + +* Patch #26682: URL-escape the ! character in generated markup for dropped uploads + === [Time tracking] +* Defect #26520: Blank "Issue" field on the "Log time" from the "Spent time - Details" page for an issue +* Defect #26667: Filtering time entries after issue's target version doesn't work as expected in some cases * Defect #26780: Translation for label_week in time report is not working === [Translations] -* Patch #27006: German translations for 3.3-stable +* Patch #26703: German translations in 3.4-stable +* Patch #27034: Patch for updated Chinese translation + +=== [UI] + +* Defect #26568: Multiple Selection List Filter View - items are cut off from view +* Patch #26395: Jump to project autocomplete: focus selected project +* Patch #26689: Add title to author's and assignee's icon === [Wiki] @@ -195,6 +313,359 @@ http://www.redmine.org/ * Defect #27186: XSS vulnerabilities +== 2017-07-16 v3.4.2 + +=== [Administration] + +* Defect #26393: Error when unchecking all settings on some plugins configurations + +=== [Attachments] + +* Defect #26379: Fix thumbnail rendering for images with height >> width + +=== [Time tracking] + +* Defect #26387: Error displaying time entries filtered by Activity + +=== [UI] + +* Defect #26445: Text formatting not applied to commit messages even if enabled in settings +* Patch #26424: Avatar Spacing in Headlines + +== 2017-07-09 v3.4.1 + +=== [Issues list] + +* Defect #26364: Sort is not reflected when export CSV of issues list + +=== [Projects] + +* Defect #26376: Wrong issue counts and spent time on project overview + +=== [Translations] + +* Patch #26344: Bulgarian translation +* Patch #26365: Traditional Chinese translation + +=== [UI] + +* Defect #26325: Wrong CSS syntax +* Defect #26350: Don't display file download button while on repository directory entries + +== 2017-07-02 v3.4.0 + +=== [Accounts / authentication] + +* Defect #13741: Not landing on home page on login after visiting lost password page +* Feature #10840: Allow "Stay logged in" from multiple browsers +* Feature #25253: Password reset should count as a password change for User#must_change_passwd +* Feature #26190: Add setting to hide optional user custom fields on registration form +* Patch #25483: Forbid to edit/update/delete the anonymous user + +=== [Activity view] + +* Patch #18399: Missing "next" pagination link when looking at yesterday's activity + +=== [Administration] + +* Defect #7577: "Send account information to the user" only works when password is set +* Defect #25289: Adding a principal to 2 projects with member inheritance leads to an error +* Feature #12598: Add tooltip on Workflow matrix for helping in big ones +* Feature #16484: Add default timezone for new users +* Feature #24780: Add tooltip on Permissions report matrix +* Feature #24790: Add tooltip on trackers summary matrix + +=== [Attachments] + +* Defect #24308: Allow Journal to return empty Array instead nil in Journal#attachments +* Feature #13072: Delete multiple attachments with one action +* Patch #22941: Allow thumbnails on documents, messages and wiki pages +* Patch #24186: Restrict the length attachment filenames on disk +* Patch #25215: Re-use existing identical disk files for new attachments +* Patch #25240: Use SHA256 for attachment digest computation +* Patch #25295: Use path instead of URL of image in preview + +=== [Code cleanup/refactoring] + +* Defect #24928: Wrong text in log/delete.me +* Defect #25563: Remove is_binary_data? from String +* Feature #15361: Use css pseudo-classes instead of cycle("odd", "even") +* Patch #24313: Use the regular "icon icon-*" classes for all elements with icons +* Patch #24382: More readable regex for parse_redmine_links +* Patch #24523: Source: ignore .idea +* Patch #24578: Remove unused CSS class ".icon-details" +* Patch #24643: Rename "issue" to "item" in query helpers +* Patch #24713: Remove iteration in ApplicationHelper#syntax_highlight_lines +* Patch #24832: Remove instance variable which is unused after r9603 +* Patch #24899: Remove unused "description_date_*" from locale files +* Patch #24900: Remove unused "label_planning" from locale files +* Patch #24901: Remove unused "label_more" from locale files +* Patch #26149: Remove duplicate method shell_quote + +=== [Core Plugins] + +* Feature #24167: Rebuild a single nested set with nested_set plugin + +=== [Custom fields] + +* Feature #6719: File format for custom fields (specific file uploads) +* Feature #16549: Set multiple values in emails for list custom fields +* Feature #23265: Group versions by status in version custom field filter +* Patch #21705: Option for long text custom fields to be displayed using full width +* Patch #24801: Flash messages on CustomFields destroy + +=== [Database] + +* Defect #23347: MySQL: You can't specify target table for update in FROM clause +* Defect #25416: "My account" broken with MySQL 8.0 (keyword admin should be escaped) + +=== [Documentation] + +* Defect #21375: Working external URL prefixes (protocols and 'www' host part) not documented in wiki syntax +* Feature #25616: Change format of the changelog (both on redmine.org and in the shipped changelog file) +* Patch #24800: Remove internal style sheet duplication and obsoleted meta tag from wiki_syntax_* documentation. +* Patch #26188: Documentation (detailed syntax help & code) additions/improvements + +=== [Email notifications] + +* Feature #25842: Add table border to email notifications +* Patch #23978: Make the email notifications for adding/updating issues more readable/clear + +=== [Email receiving] + +* Defect #25256: Mail parts with empty content should be ignored +* Feature #5864: Regex Text on Receiver Email +* Patch #17718: Body delimiters to truncate emails do not take uncommon whitespace into account + +=== [Forums] + +* Patch #24535: Flash messages on Board destroy + +=== [Gantt] + +* Patch #25876: Gantt chart shows % done even if the field is disabled for the tracker + +=== [Gems support] + +* Feature #23932: Update TinyTds to recent version (1.0.5) +* Feature #25781: Markdown: Upgrade redcarpet gem to 3.4 + +=== [Hook requests] + +* Patch #23545: Add before_render hook to WikiController#show + +=== [I18n] + +* Defect #24616: Should not replace all invalid utf8 characters (e.g in mail) +* Patch #24938: Update tr.yml for general_first_day_of_week +* Patch #25014: redmine/i18n.rb - languages_lookup class variable is rebuilt every time + +=== [Importers] + +* Feature #22701: Allow forward reference to parent when importing issues + +=== [Issues] + +* Defect #5385: Status filter should show statuses related to project trackers only +* Defect #15226: Searching for issues with "updated = none" always returns zero results +* Defect #16260: Add Subtask does not work correctly from tasks with Parent Task field disabled +* Defect #17632: Users can't see private notes created by themselves if "Mark notes as private" is set but "View private notes" is not +* Defect #17762: When copying an issue and changing the project, the list of watchers is not updated +* Defect #20127: The description column in the issues table is too short (MySQL) +* Defect #21579: The cancel operation in the issue edit mode doesn't work +* Defect #23511: Progress of parent task should be calculated using total estimated hours of children +* Defect #23755: Bulk edit form not show fields based on target tracker and status +* Feature #482: Default assignee on each project +* Feature #3425: View progress bar of related issues +* Feature #10460: Option to copy watchers when copying issues +* Feature #10989: Prevent parent issue from being closed if a child issue is open +* Feature #12706: Ability to change the private flag when editing a note +* Feature #20279: Allow to filter issues with "Any" or "None" target version defined when viewing all issues +* Feature #21623: Journalize values that are cleared after project or tracker change +* Feature #22600: Add warning when loosing data from custom fields when bulk editing issues +* Feature #23610: Reset status when copying issues +* Feature #24015: Do not hide estimated_hours label when value is nil +* Feature #25052: Allow to disable description field in tracker setting +* Patch #23888: Show an error message when changing an issue's project fails due to errors in child issues +* Patch #24692: Issue destroy : Reassign time issue autocomplete +* Patch #24877: Filter parent task issues in auto complete by open/closed status depending on the subtask status +* Patch #25055: Filter out current issue from the related issues autocomplete + +=== [Issues filter] + +* Defect #24769: User custom field filter lists only "Me" on cross project issue list +* Defect #24907: Issue queries: "Default columns" option conflicts with "Show description" +* Defect #25077: Issue description filter's 'none' operator does not match issues with blank descriptions +* Feature #2783: Filter issues by attachments +* Feature #10412: Target version filter shoud group versions by status +* Feature #15773: Filtering out specific subprojects (using 'is not' operator) +* Feature #17720: Filter issues by "Updated by" and "Last updated by" +* Feature #21249: Ability to filter issues by attributes of a version custom field (e.g. release date) +* Feature #23215: Add the possibility to filter issues after Target Version's Status and Due Date + +=== [Issues list] + +* Feature #1474: Show last comment/notes in the issue list +* Feature #6375: Last updated by colum in issue list +* Feature #25515: View attachments on the issue list +* Patch #24649: Make Spent time clickable in issue lists + +=== [Issues workflow] + +* Defect #14696: Limited status when copying an issue +* Patch #24281: Workflow editing shows statuses of irrelevant roles + +=== [My page] + +* Feature #1565: Custom query on My page +* Feature #7769: Sortable columns in issue lists on "My page" +* Feature #8761: My page - Spent time section only display 7 days, make it a parameter +* Feature #23459: Columns selection on the issues lists on "My page" +* Feature #25297: In place editing of "My page" layout + +=== [Performance] + +* Defect #24433: The changeset display is slow when changeset_issues has very many records +* Feature #23743: Add index to workflows.tracker_id +* Feature #23987: Add an index on issues.parent_id +* Patch #21608: Project#allowed_to_condition performance +* Patch #22850: Speedup remove_inherited_roles +* Patch #23519: Don't preload projects and roles on Principal#memberships association +* Patch #24587: Improve custom fields list performance +* Patch #24787: Don't preload all filter values when displaying issues/time entries +* Patch #24839: Minor performance improvement - Replace count by exists? +* Patch #24865: Load associations of query results more efficiently +* Patch #25022: Add an index on attachments.disk_filename + +=== [Permissions and roles] + +* Feature #4866: New permission: view forum +* Feature #7068: New permission: view news + +=== [Project settings] + +* Defect #23470: Disable "Select project modules" permission does not apply to the new project form +* Feature #22608: Enable filtering versions on Project -> Settings -> Versions +* Feature #24011: Add option to set a new version as default directly from New Version page + +=== [REST API] + +* Defect #23921: REST API Issue PUT responds 200 OK even when it can't set assigned_to_id +* Feature #7506: Include allowed activities list in "project" API response +* Feature #12181: Add attachment information to issues.xml in REST API +* Feature #23566: REST API should return attachment's id in addition to token +* Patch #19116: Files REST API +* Patch #22356: Add support for updating attachments over REST API +* Patch #22795: Render custom field values of enumerations in API requests + +=== [Roadmap] + +* Defect #23377: Don't show "status" field when creating a new version +* Feature #23137: Completed versions on Roadmap: Sort it so that recently created versions are on top + +=== [Ruby support] + +* Feature #25048: Ruby 2.4 support + +=== [SCM] + +* Defect #14626: Repositories' extra_info column is too short with MySQL + +=== [SCM extra] + +* Defect #23865: Typo: s/projet/project/ in Redmine.pm comments + +=== [Search engine] + +* Feature #9909: Search in project and its subprojects by default + +=== [Text formatting] + +* Defect #26310: "attachment:filename" should generate a link to preview instead of download +* Feature #4179: Link to user in wiki syntax +* Feature #22758: Make text formatting of commit messages optional +* Feature #24922: Support high resolution images in formatted content +* Patch #26157: Render all possible inline textile images + +=== [Themes] + +* Defect #25118: ThemesTest#test_without_theme_js may fail if third-party theme is installed + +=== [Time tracking] + +* Defect #13653: Keep displaying spent time page when switching project via dropdown menu +* Defect #23912: No validation error when date value is invalid in time entries filter +* Defect #24041: Issue subject is not updated when you select another issue in the new "Log time" page +* Feature #588: Move timelog between projects +* Feature #13558: Add version filter in spent time report +* Feature #14790: Ability to save spent time query filters +* Feature #16843: Enable grouping on time entries list +* Feature #23401: Add tracker and status columns/filters to detailed timelog +* Feature #24157: Make project custom fields available in timelogs columns +* Feature #24577: Settings to make the issue and/or comment fields mandatory for time logs +* Patch #24189: Time entry form - limit issue autocomplete to already selected project + +=== [Translations] + +* Defect #25470: Fix Japanese mistranslation for field_base_dn +* Defect #25687: Bad translation in french for indentation +* Patch #23108: Change Japanese translation for text_git_repository_note +* Patch #23250: Fixes issues with Catalan translation +* Patch #23359: Change Japanese translation for label_commits_per_author +* Patch #23388: German translation change +* Patch #23419: Change Japanese translation for label_display_used_statuses_only +* Patch #23659: Change Japanese translation for label_enumerations +* Patch #23806: Fix Japanese translation inconsistency of label_tracker_new and label_custom_field_new +* Patch #24174: Change Japanese translation for "format" +* Patch #24177: Change translation for label_user_mail_option_only_(assigned|owner) +* Patch #24268: Wrong German translation of logging time error message +* Patch #24407: Dutch (NL) translation enhancements and complete review (major update) +* Patch #24494: Spanish Panama "label_issue_new" translation change +* Patch #24518: Spanish translation change (adding accent mark and caps) +* Patch #24572: Spanish label_search_open_issues_only: translation change +* Patch #24750: Change Japanese translation for setting_text_formatting and setting_cache_formatted_text +* Patch #24891: Change Japanese translation for "items" +* Patch #25019: Localization for Ukrainian language - completed +* Patch #25204: Portuguese translation file +* Patch #25392: Change Russian translation for field_due_date and label_relation_new +* Patch #25609: Change Japanese translation for field_attr_* +* Patch #25628: Better wording for issue update conflict resolution in German +* Patch #26180: Change Russian translation for "Estimated time" + +=== [UI] + +* Defect #23575: Issue subjects are truncated at 60 characters on activity page +* Defect #23840: Reduce the maximum height of the issue description field +* Defect #23979: Elements are not aligned properly in issues table for some cases +* Defect #24617: Browser js/css cache remains after upgrade +* Feature #5920: Unify and improve cross-project views layout +* Feature #9850: Differentiate shared versions in version-format custom field drop-downs by prepending its project name +* Feature #10250: Renaming "duplicates" and "duplicated by" to something less confusing +* Feature #23310: Improved "jump to project" drop-down +* Feature #23311: New "Spent time" menu tab when spent time module is enabled on project +* Feature #23653: User preference for monospaced / variable-width font in textareas +* Feature #23996: Introduce a setting to change the display format of timespans to HH:MM +* Feature #24720: Move all 'new item' links in project settings to above the item tables +* Feature #24927: Render high resolution Gravatars and Thumbnails +* Feature #25988: Preview files by default instead of downloading them +* Feature #25999: View repository content by default (instead of the history) +* Feature #26035: More visually consistent download links +* Feature #26071: Generate markup for uploaded image dropped into wiki-edit textarea +* Feature #26189: For 3 comments or more on news items and forum messages, show reply link at top of comments as well +* Patch #23146: Show revision details using the same structure and look from the journals details +* Patch #23192: Add the new pagination style in the activity page +* Patch #23639: Add "Log time" to global button menu (+) +* Patch #23998: Added link to author in Repository +* Patch #24776: UI inconsistencies on /enumerations/index view +* Patch #24833: Always show "Jump to project" drop-down +* Patch #25320: Remove initial indentation of blockquotes for better readability +* Patch #25775: Show assignee's icon in addition to author's icon + +=== [Wiki] + +* Feature #12183: Hide attachments by default on wiki pages +* Feature #23179: Add heading to table of contents macro + == 2017-07-02 v3.3.4 === [Accounts / authentication] diff --git a/doc/INSTALL b/doc/INSTALL index 8d4b4b5f7..a66a8541d 100644 --- a/doc/INSTALL +++ b/doc/INSTALL @@ -1,7 +1,7 @@ == Redmine installation Redmine - project management software -Copyright (C) 2006-2016 Jean-Philippe Lang +Copyright (C) 2006-2017 Jean-Philippe Lang http://www.redmine.org/ diff --git a/doc/README_FOR_APP b/doc/README_FOR_APP index e4c7367ad..8881e30fe 100644 --- a/doc/README_FOR_APP +++ b/doc/README_FOR_APP @@ -6,7 +6,7 @@ More details can be found at http://www.redmine.org = License -Copyright (C) 2006-2016 Jean-Philippe Lang +Copyright (C) 2006-2017 Jean-Philippe Lang This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/doc/UPGRADING b/doc/UPGRADING index 6b4c7860c..ac7862485 100644 --- a/doc/UPGRADING +++ b/doc/UPGRADING @@ -1,7 +1,7 @@ == Redmine upgrade Redmine - project management software -Copyright (C) 2006-2016 Jean-Philippe Lang +Copyright (C) 2006-2017 Jean-Philippe Lang http://www.redmine.org/ diff --git a/extra/mail_handler/rdm-mailhandler.rb b/extra/mail_handler/rdm-mailhandler.rb index 5d4a10632..49fb13cff 100644 --- a/extra/mail_handler/rdm-mailhandler.rb +++ b/extra/mail_handler/rdm-mailhandler.rb @@ -1,6 +1,6 @@ #!/usr/bin/env ruby # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -117,7 +117,7 @@ Overrides: Examples: No project specified, emails MUST contain the 'Project' keyword, otherwise - they will be dropped (not recommanded): + they will be dropped (not recommended): rdm-mailhandler.rb --url http://redmine.domain.foo --key secret @@ -165,7 +165,7 @@ END_DESC begin response = Net::HTTPS.post_form(URI.parse(uri), data, headers, :no_check_certificate => no_check_certificate, :certificate_bundle => certificate_bundle) rescue SystemCallError, IOError => e # connection refused, etc. - warn "An error occured while contacting your Redmine server: #{e.message}" + warn "An error occurred while contacting your Redmine server: #{e.message}" return 75 # temporary failure end debug "Response received: #{response.code}" diff --git a/extra/sample_plugin/app/controllers/example_controller.rb b/extra/sample_plugin/app/controllers/example_controller.rb index dbc271a1b..d38d01d91 100644 --- a/extra/sample_plugin/app/controllers/example_controller.rb +++ b/extra/sample_plugin/app/controllers/example_controller.rb @@ -1,9 +1,7 @@ # Sample plugin controller class ExampleController < ApplicationController - unloadable - layout 'base' - before_filter :find_project, :authorize + before_action :find_project, :authorize menu_item :sample_plugin def say_hello diff --git a/extra/svn/Redmine.pm b/extra/svn/Redmine.pm index 0cb99b632..96b2aa63d 100644 --- a/extra/svn/Redmine.pm +++ b/extra/svn/Redmine.pm @@ -58,7 +58,7 @@ Authen::Simple::LDAP (and IO::Socket::SSL if LDAPS is used): RedmineDbUser "redmine" RedmineDbPass "password" ## Optional where clause (fulltext search would be slow and - ## database dependant). + ## database dependent). # RedmineDbWhereClause "and members.role_id IN (1,2)" ## Optional credentials cache size # RedmineCacheCredsMax 50 @@ -84,7 +84,7 @@ and you will have to use this reposman.rb command line to create repository : =head1 REPOSITORIES NAMING -A projet repository must be named with the projet identifier. In case +A project repository must be named with the project identifier. In case of multiple repositories for the same project, use the project identifier and the repository identifier separated with a dot: diff --git a/extra/svn/reposman.rb b/extra/svn/reposman.rb index d79ad2695..4f748acf0 100755 --- a/extra/svn/reposman.rb +++ b/extra/svn/reposman.rb @@ -5,7 +5,7 @@ require 'find' require 'etc' require 'rubygems' -Version = "1.4" +Version = "1.5" SUPPORTED_SCM = %w( Subversion Darcs Mercurial Bazaar Git Filesystem ) $verbose = 0 @@ -160,10 +160,10 @@ end class Project < ActiveResource::Base self.headers["User-agent"] = "Redmine repository manager/#{Version}" - self.format = :xml + self.format = :json end -log("querying Redmine for projects...", :level => 1); +log("querying Redmine for active projects with repository module enabled...", :level => 1); $redmine_host.gsub!(/^/, "http://") unless $redmine_host.match("^https?://") $redmine_host.gsub!(/\/$/, '') @@ -214,8 +214,6 @@ def mswin? end projects.each do |project| - log("treating project #{project.name}", :level => 1) - if project.identifier.empty? log("\tno identifier for project #{project.name}") next @@ -223,6 +221,7 @@ projects.each do |project| log("\tinvalid identifier for project #{project.name} : #{project.identifier}"); next; end + log("processing project #{project.identifier} (#{project.name})", :level => 1) repos_path = File.join($repos_base, project.identifier).gsub(File::SEPARATOR, File::ALT_SEPARATOR || File::SEPARATOR) @@ -258,7 +257,7 @@ projects.each do |project| project.is_public ? File.umask(0002) : File.umask(0007) if $test - log("\tcreate repository #{repos_path}") + log("\trepository #{repos_path} created") log("\trepository #{repos_path} registered in Redmine with url #{$svn_url}#{project.identifier}") if $svn_url; next end @@ -275,6 +274,7 @@ projects.each do |project| log("\tunable to create #{repos_path} : #{e}\n") next end + log("\trepository #{repos_path} created"); if $svn_url begin @@ -284,6 +284,5 @@ projects.each do |project| log("\trepository #{repos_path} not registered in Redmine: #{e.message}"); end end - log("\trepository #{repos_path} created"); end end diff --git a/lib/SVG/Graph/Graph.rb b/lib/SVG/Graph/Graph.rb index f048ac382..f5b0f61bf 100644 --- a/lib/SVG/Graph/Graph.rb +++ b/lib/SVG/Graph/Graph.rb @@ -234,7 +234,7 @@ module SVG # scales to fix the space. attr_accessor :width # Set the path to an external stylesheet, set to '' if - # you want to revert back to using the defaut internal version. + # you want to revert back to using the default internal version. # # To create an external stylesheet create a graph using the # default internal version and copy the stylesheet section to diff --git a/lib/SVG/Graph/Schedule.rb b/lib/SVG/Graph/Schedule.rb index 70bb519d7..87d12cb53 100644 --- a/lib/SVG/Graph/Schedule.rb +++ b/lib/SVG/Graph/Schedule.rb @@ -51,7 +51,7 @@ module SVG # # = Notes # - # The default stylesheet handles upto 10 data sets, if you + # The default stylesheet handles up to 10 data sets, if you # use more you must create your own stylesheet and add the # additional settings for the extra data sets. You will know # if you go over 10 data sets as they will have no style and @@ -138,7 +138,7 @@ module SVG # Note that the data must be in time,value pairs, and that the date format # may be any date that is parseable by ParseDate. # Also note that, in this example, we're mixing scales; the data from d1 - # will probably not be discernable if both data sets are plotted on the same + # will probably not be discernible if both data sets are plotted on the same # graph, since d1 is too granular. def add_data data @data = [] unless @data diff --git a/lib/generators/redmine_plugin/redmine_plugin_generator.rb b/lib/generators/redmine_plugin/redmine_plugin_generator.rb index 562366342..ce2e2aa42 100644 --- a/lib/generators/redmine_plugin/redmine_plugin_generator.rb +++ b/lib/generators/redmine_plugin/redmine_plugin_generator.rb @@ -7,7 +7,7 @@ class RedminePluginGenerator < Rails::Generators::NamedBase super @plugin_name = file_name.underscore @plugin_pretty_name = plugin_name.titleize - @plugin_path = "plugins/#{plugin_name}" + @plugin_path = File.join(Redmine::Plugin.directory, plugin_name) end def copy_templates diff --git a/lib/generators/redmine_plugin_controller/redmine_plugin_controller_generator.rb b/lib/generators/redmine_plugin_controller/redmine_plugin_controller_generator.rb index 8351ce681..9c42d938b 100644 --- a/lib/generators/redmine_plugin_controller/redmine_plugin_controller_generator.rb +++ b/lib/generators/redmine_plugin_controller/redmine_plugin_controller_generator.rb @@ -9,7 +9,7 @@ class RedminePluginControllerGenerator < Rails::Generators::NamedBase super @plugin_name = file_name.underscore @plugin_pretty_name = plugin_name.titleize - @plugin_path = "plugins/#{plugin_name}" + @plugin_path = File.join(Redmine::Plugin.directory, plugin_name) @controller_class = controller.camelize end diff --git a/lib/generators/redmine_plugin_controller/templates/controller.rb.erb b/lib/generators/redmine_plugin_controller/templates/controller.rb.erb index dddbf7017..0fc5e5c8f 100644 --- a/lib/generators/redmine_plugin_controller/templates/controller.rb.erb +++ b/lib/generators/redmine_plugin_controller/templates/controller.rb.erb @@ -1,6 +1,4 @@ class <%= @controller_class %>Controller < ApplicationController - unloadable - <% actions.each do |action| -%> def <%= action %> diff --git a/lib/generators/redmine_plugin_model/redmine_plugin_model_generator.rb b/lib/generators/redmine_plugin_model/redmine_plugin_model_generator.rb index 2e5f03108..f26f8779c 100644 --- a/lib/generators/redmine_plugin_model/redmine_plugin_model_generator.rb +++ b/lib/generators/redmine_plugin_model/redmine_plugin_model_generator.rb @@ -14,7 +14,7 @@ class RedminePluginModelGenerator < Rails::Generators::NamedBase super @plugin_name = file_name.underscore @plugin_pretty_name = plugin_name.titleize - @plugin_path = "plugins/#{plugin_name}" + @plugin_path = File.join(Redmine::Plugin.directory, plugin_name) @model_class = model.camelize @table_name = @model_class.tableize @migration_filename = "create_#{@table_name}" diff --git a/lib/generators/redmine_plugin_model/templates/model.rb.erb b/lib/generators/redmine_plugin_model/templates/model.rb.erb index 71284e96d..cb6f31411 100644 --- a/lib/generators/redmine_plugin_model/templates/model.rb.erb +++ b/lib/generators/redmine_plugin_model/templates/model.rb.erb @@ -1,3 +1,2 @@ class <%= @model_class %> < ActiveRecord::Base - unloadable end diff --git a/lib/plugins/acts_as_activity_provider/lib/acts_as_activity_provider.rb b/lib/plugins/acts_as_activity_provider/lib/acts_as_activity_provider.rb index 8f65eb720..2bf4b2e8c 100644 --- a/lib/plugins/acts_as_activity_provider/lib/acts_as_activity_provider.rb +++ b/lib/plugins/acts_as_activity_provider/lib/acts_as_activity_provider.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -48,7 +48,7 @@ module Redmine end module ClassMethods - # Returns events of type event_type visible by user that occured between from and to + # Returns events of type event_type visible by user that occurred between from and to def find_events(event_type, user, from, to, options) provider_options = activity_provider_options[event_type] raise "#{self.name} can not provide #{event_type} events." if provider_options.nil? diff --git a/lib/plugins/acts_as_attachable/lib/acts_as_attachable.rb b/lib/plugins/acts_as_attachable/lib/acts_as_attachable.rb index a6ca09170..36996c950 100644 --- a/lib/plugins/acts_as_attachable/lib/acts_as_attachable.rb +++ b/lib/plugins/acts_as_attachable/lib/acts_as_attachable.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -34,6 +34,7 @@ module Redmine options.merge(:as => :container, :dependent => :destroy, :inverse_of => :container) send :include, Redmine::Acts::Attachable::InstanceMethods before_save :attach_saved_attachments + after_rollback :detach_saved_attachments validate :warn_about_failed_attachments end end @@ -89,7 +90,7 @@ module Redmine a = nil if file = attachment['file'] a = Attachment.create(:file => file, :author => author) - elsif token = attachment['token'] + elsif token = attachment['token'].presence a = Attachment.find_by_token(token) unless a @failed_attachment_count += 1 @@ -116,6 +117,14 @@ module Redmine end end + def detach_saved_attachments + saved_attachments.each do |attachment| + # TODO: use #reload instead, after upgrading to Rails 5 + # (after_rollback is called when running transactional tests in Rails 4) + attachment.container = nil + end + end + def warn_about_failed_attachments if @failed_attachment_count && @failed_attachment_count > 0 errors.add :base, ::I18n.t('warning_attachments_not_saved', count: @failed_attachment_count) diff --git a/lib/plugins/acts_as_customizable/lib/acts_as_customizable.rb b/lib/plugins/acts_as_customizable/lib/acts_as_customizable.rb index 050e442b4..e7805cbcf 100644 --- a/lib/plugins/acts_as_customizable/lib/acts_as_customizable.rb +++ b/lib/plugins/acts_as_customizable/lib/acts_as_customizable.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -29,6 +29,7 @@ module Redmine self.customizable_options = options has_many :custom_values, lambda {includes(:custom_field).order("#{CustomField.table_name}.position")}, :as => :customized, + :inverse_of => :customized, :dependent => :delete_all, :validate => false @@ -41,7 +42,6 @@ module Redmine module InstanceMethods def self.included(base) base.extend ClassMethods - base.send :alias_method_chain, :reload, :custom_fields end def available_custom_fields @@ -69,16 +69,7 @@ module Redmine custom_field_values.each do |custom_field_value| key = custom_field_value.custom_field_id.to_s if values.has_key?(key) - value = values[key] - if value.is_a?(Array) - value = value.reject(&:blank?).map(&:to_s).uniq - if value.empty? - value << '' - end - else - value = value.to_s - end - custom_field_value.value = value + custom_field_value.value = values[key] end end @custom_field_values_changed = true @@ -94,11 +85,11 @@ module Redmine if values.empty? values << custom_values.build(:customized => self, :custom_field => field) end - x.value = values.map(&:value) + x.instance_variable_set("@value", values.map(&:value)) else cv = custom_values.detect { |v| v.custom_field == field } cv ||= custom_values.build(:customized => self, :custom_field => field) - x.value = cv.value + x.instance_variable_set("@value", cv.value) end x.value_was = x.value.dup if x.value x @@ -164,10 +155,10 @@ module Redmine @custom_field_values_changed = true end - def reload_with_custom_fields(*args) + def reload(*args) @custom_field_values = nil @custom_field_values_changed = false - reload_without_custom_fields(*args) + super end module ClassMethods diff --git a/lib/plugins/acts_as_event/lib/acts_as_event.rb b/lib/plugins/acts_as_event/lib/acts_as_event.rb index 37b326b8e..894a23df6 100644 --- a/lib/plugins/acts_as_event/lib/acts_as_event.rb +++ b/lib/plugins/acts_as_event/lib/acts_as_event.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/plugins/acts_as_searchable/lib/acts_as_searchable.rb b/lib/plugins/acts_as_searchable/lib/acts_as_searchable.rb index be8907188..cc4d4a2d8 100644 --- a/lib/plugins/acts_as_searchable/lib/acts_as_searchable.rb +++ b/lib/plugins/acts_as_searchable/lib/acts_as_searchable.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -167,7 +167,7 @@ module Redmine scope. reorder(searchable_options[:date_column] => :desc, :id => :desc). limit(limit). - uniq. + distinct. pluck(searchable_options[:date_column], :id). # converts timestamps to integers for faster sort map {|timestamp, id| [timestamp.to_i, id]} diff --git a/lib/plugins/acts_as_watchable/lib/acts_as_watchable.rb b/lib/plugins/acts_as_watchable/lib/acts_as_watchable.rb index 4bbba854a..2bddd64b7 100644 --- a/lib/plugins/acts_as_watchable/lib/acts_as_watchable.rb +++ b/lib/plugins/acts_as_watchable/lib/acts_as_watchable.rb @@ -20,7 +20,6 @@ module Redmine attr_protected :watcher_ids, :watcher_user_ids end send :include, Redmine::Acts::Watchable::InstanceMethods - alias_method_chain :watcher_user_ids=, :uniq_ids end end @@ -59,11 +58,11 @@ module Redmine end # Overrides watcher_user_ids= to make user_ids uniq - def watcher_user_ids_with_uniq_ids=(user_ids) + def watcher_user_ids=(user_ids) if user_ids.is_a?(Array) user_ids = user_ids.uniq end - send :watcher_user_ids_without_uniq_ids=, user_ids + super user_ids end # Returns true if object is watched by +user+ diff --git a/lib/plugins/gravatar/lib/gravatar.rb b/lib/plugins/gravatar/lib/gravatar.rb index 93c45b25c..5d9cda340 100644 --- a/lib/plugins/gravatar/lib/gravatar.rb +++ b/lib/plugins/gravatar/lib/gravatar.rb @@ -52,7 +52,11 @@ module GravatarHelper src = h(gravatar_url(email, options)) options = DEFAULT_OPTIONS.merge(options) [:class, :alt, :title].each { |opt| options[opt] = h(options[opt]) } - image_tag src, options + + # double the size for hires displays + options[:srcset] = "#{gravatar_url(email, options.merge(size: options[:size].to_i * 2))} 2x" + + image_tag src, options.except(:rating, :size, :default, :ssl) end # Returns the base Gravatar URL for the given email hash diff --git a/lib/redmine.rb b/lib/redmine.rb index 71722cc8e..75c455a07 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -42,7 +42,6 @@ require 'redmine/menu_manager' require 'redmine/notifiable' require 'redmine/platform' require 'redmine/mime_type' -require 'redmine/notifiable' require 'redmine/search' require 'redmine/syntax_highlighting' require 'redmine/thumbnail' @@ -83,9 +82,12 @@ Redmine::AccessControl.map do |map| map.permission :close_project, {:projects => [:close, :reopen]}, :require => :member, :read => true map.permission :select_project_modules, {:projects => :modules}, :require => :member map.permission :view_members, {:members => [:index, :show]}, :public => true, :read => true - map.permission :manage_members, {:projects => :settings, :members => [:index, :show, :new, :create, :update, :destroy, :autocomplete]}, :require => :member + map.permission :manage_members, {:projects => :settings, :members => [:index, :show, :new, :create, :edit, :update, :destroy, :autocomplete]}, :require => :member map.permission :manage_versions, {:projects => :settings, :versions => [:new, :create, :edit, :update, :close_completed, :destroy]}, :require => :member map.permission :add_subprojects, {:projects => [:new, :create]}, :require => :member + # Queries + map.permission :manage_public_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :member + map.permission :save_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :loggedin map.project_module :issue_tracking do |map| # Issues @@ -110,9 +112,6 @@ Redmine::AccessControl.map do |map| map.permission :view_private_notes, {}, :read => true, :require => :member map.permission :set_notes_private, {}, :require => :member map.permission :delete_issues, {:issues => :destroy}, :require => :member - # Queries - map.permission :manage_public_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :member - map.permission :save_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :loggedin # Watchers map.permission :view_issue_watchers, {}, :read => true map.permission :add_issue_watchers, {:watchers => [:new, :create, :append, :autocomplete_for_user]} @@ -127,11 +126,11 @@ Redmine::AccessControl.map do |map| 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_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 map.project_module :news do |map| - map.permission :view_news, {:news => [:index, :show]}, :public => true, :read => true + map.permission :view_news, {:news => [:index, :show]}, :read => true map.permission :manage_news, {:news => [:new, :create, :edit, :update, :destroy], :comments => [:destroy], :attachments => :upload}, :require => :member map.permission :comment_news, {:comments => :create} end @@ -165,17 +164,17 @@ Redmine::AccessControl.map do |map| map.permission :browse_repository, {:repositories => [:show, :browse, :entry, :raw, :annotate, :changes, :diff, :stats, :graph]}, :read => true map.permission :commit_access, {} 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 map.project_module :boards do |map| - map.permission :view_messages, {:boards => [:index, :show], :messages => [:show]}, :public => true, :read => true + map.permission :view_messages, {:boards => [:index, :show], :messages => [:show]}, :read => true map.permission :add_messages, {:messages => [:new, :reply, :quote], :attachments => :upload} map.permission :edit_messages, {:messages => :edit, :attachments => :upload}, :require => :member map.permission :edit_own_messages, {:messages => :edit, :attachments => :upload}, :require => :loggedin map.permission :delete_messages, {:messages => :destroy}, :require => :member 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 map.project_module :calendar do |map| @@ -203,26 +202,52 @@ Redmine::MenuManager.map :account_menu do |menu| end Redmine::MenuManager.map :application_menu do |menu| - # Empty + menu.push :projects, {:controller => 'projects', :action => 'index'}, + :permission => nil, + :caption => :label_project_plural + menu.push :activity, {:controller => 'activities', :action => 'index'} + menu.push :issues, {:controller => 'issues', :action => 'index'}, + :if => Proc.new {User.current.allowed_to?(:view_issues, nil, :global => true)}, + :caption => :label_issue_plural + menu.push :time_entries, {:controller => 'timelog', :action => 'index'}, + :if => Proc.new {User.current.allowed_to?(:view_time_entries, nil, :global => true)}, + :caption => :label_spent_time + menu.push :gantt, { :controller => 'gantts', :action => 'show' }, :caption => :label_gantt, + :if => Proc.new {User.current.allowed_to?(:view_gantt, nil, :global => true)} + menu.push :calendar, { :controller => 'calendars', :action => 'show' }, :caption => :label_calendar, + :if => Proc.new {User.current.allowed_to?(:view_calendar, nil, :global => true)} + menu.push :news, {:controller => 'news', :action => 'index'}, + :if => Proc.new {User.current.allowed_to?(:view_news, nil, :global => true)}, + :caption => :label_news_plural end Redmine::MenuManager.map :admin_menu do |menu| - menu.push :projects, {:controller => 'admin', :action => 'projects'}, :caption => :label_project_plural - menu.push :users, {:controller => 'users'}, :caption => :label_user_plural - menu.push :groups, {:controller => 'groups'}, :caption => :label_group_plural - menu.push :roles, {:controller => 'roles'}, :caption => :label_role_and_permissions - menu.push :trackers, {:controller => 'trackers'}, :caption => :label_tracker_plural + menu.push :projects, {:controller => 'admin', :action => 'projects'}, :caption => :label_project_plural, + :html => {:class => 'icon icon-projects'} + menu.push :users, {:controller => 'users'}, :caption => :label_user_plural, + :html => {:class => 'icon icon-user'} + menu.push :groups, {:controller => 'groups'}, :caption => :label_group_plural, + :html => {:class => 'icon icon-group'} + menu.push :roles, {:controller => 'roles'}, :caption => :label_role_and_permissions, + :html => {:class => 'icon icon-roles'} + menu.push :trackers, {:controller => 'trackers'}, :caption => :label_tracker_plural, + :html => {:class => 'icon icon-issue'} menu.push :issue_statuses, {:controller => 'issue_statuses'}, :caption => :label_issue_status_plural, - :html => {:class => 'issue_statuses'} - menu.push :workflows, {:controller => 'workflows', :action => 'edit'}, :caption => :label_workflow + :html => {:class => 'icon icon-issue-edit'} + menu.push :workflows, {:controller => 'workflows', :action => 'edit'}, :caption => :label_workflow, + :html => {:class => 'icon icon-workflows'} menu.push :custom_fields, {:controller => 'custom_fields'}, :caption => :label_custom_field_plural, - :html => {:class => 'custom_fields'} - menu.push :enumerations, {:controller => 'enumerations'} - menu.push :settings, {:controller => 'settings'} + :html => {:class => 'icon icon-custom-fields'} + menu.push :enumerations, {:controller => 'enumerations'}, + :html => {:class => 'icon icon-list'} + menu.push :settings, {:controller => 'settings'}, + :html => {:class => 'icon icon-settings'} menu.push :ldap_authentication, {:controller => 'auth_sources', :action => 'index'}, - :html => {:class => 'server_authentication'} - menu.push :plugins, {:controller => 'admin', :action => 'plugins'}, :last => true - menu.push :info, {:controller => 'admin', :action => 'info'}, :caption => :label_information_plural, :last => true + :html => {:class => 'icon icon-server-authentication'} + menu.push :plugins, {:controller => 'admin', :action => 'plugins'}, :last => true, + :html => {:class => 'icon icon-plugins'} + menu.push :info, {:controller => 'admin', :action => 'info'}, :caption => :label_information_plural, :last => true, + :html => {:class => 'icon icon-help'} end Redmine::MenuManager.map :project_menu do |menu| @@ -238,6 +263,8 @@ Redmine::MenuManager.map :project_menu do |menu| :parent => :new_object menu.push :new_version, {:controller => 'versions', :action => 'new'}, :param => :project_id, :caption => :label_version_new, :parent => :new_object + menu.push :new_timelog, {:controller => 'timelog', :action => 'new'}, :param => :project_id, :caption => :button_log_time, + :parent => :new_object menu.push :new_news, {:controller => 'news', :action => 'new'}, :param => :project_id, :caption => :label_news_new, :parent => :new_object menu.push :new_document, {:controller => 'documents', :action => 'new'}, :param => :project_id, :caption => :label_document_new, @@ -246,6 +273,7 @@ Redmine::MenuManager.map :project_menu do |menu| :parent => :new_object menu.push :new_file, {:controller => 'files', :action => 'new'}, :param => :project_id, :caption => :label_attachment_new, :parent => :new_object + menu.push :overview, { :controller => 'projects', :action => 'show' } menu.push :activity, { :controller => 'activities', :action => 'index' } menu.push :roadmap, { :controller => 'versions', :action => 'index' }, :param => :project_id, @@ -255,6 +283,7 @@ Redmine::MenuManager.map :project_menu do |menu| :html => { :accesskey => Redmine::AccessKeys.key_for(:new_issue) }, :if => Proc.new { |p| Setting.new_item_menu_tab == '1' && Issue.allowed_target_trackers(p).any? }, :permission => :add_issues + menu.push :time_entries, { :controller => 'timelog', :action => 'index' }, :param => :project_id, :caption => :label_spent_time menu.push :gantt, { :controller => 'gantts', :action => 'show' }, :param => :project_id, :caption => :label_gantt menu.push :calendar, { :controller => 'calendars', :action => 'show' }, :param => :project_id, :caption => :label_calendar menu.push :news, { :controller => 'news', :action => 'index' }, :param => :project_id, :caption => :label_news_plural diff --git a/lib/redmine/access_control.rb b/lib/redmine/access_control.rb index e01cb2598..abf88f48e 100644 --- a/lib/redmine/access_control.rb +++ b/lib/redmine/access_control.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/access_keys.rb b/lib/redmine/access_keys.rb index 302046a78..f2616061b 100644 --- a/lib/redmine/access_keys.rb +++ b/lib/redmine/access_keys.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/activity.rb b/lib/redmine/activity.rb index 716a89988..48e02dffa 100644 --- a/lib/redmine/activity.rb +++ b/lib/redmine/activity.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/activity/fetcher.rb b/lib/redmine/activity/fetcher.rb index 627a08441..fccce54eb 100644 --- a/lib/redmine/activity/fetcher.rb +++ b/lib/redmine/activity/fetcher.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/acts/positioned.rb b/lib/redmine/acts/positioned.rb index 75041fe38..21cc14a67 100644 --- a/lib/redmine/acts/positioned.rb +++ b/lib/redmine/acts/positioned.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/ciphering.rb b/lib/redmine/ciphering.rb index 9257336a6..61f309b40 100644 --- a/lib/redmine/ciphering.rb +++ b/lib/redmine/ciphering.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -26,7 +26,7 @@ module Redmine if cipher_key.blank? || text.blank? text else - c = OpenSSL::Cipher::Cipher.new("aes-256-cbc") + c = OpenSSL::Cipher.new("aes-256-cbc") iv = c.random_iv c.encrypt c.key = cipher_key @@ -44,7 +44,7 @@ module Redmine return text end text = match[1] - c = OpenSSL::Cipher::Cipher.new("aes-256-cbc") + c = OpenSSL::Cipher.new("aes-256-cbc") e, iv = text.split("--").map {|s| Base64.decode64(s)} c.decrypt c.key = cipher_key @@ -58,9 +58,9 @@ module Redmine def cipher_key key = Redmine::Configuration['database_cipher_key'].to_s - key.blank? ? nil : Digest::SHA256.hexdigest(key) + key.blank? ? nil : Digest::SHA256.hexdigest(key)[0..31] end - + def logger Rails.logger end @@ -84,8 +84,8 @@ module Redmine object.send :write_attribute, attribute, clear raise(ActiveRecord::Rollback) unless object.save(:validation => false) end - end - end ? true : false + end ? true : false + end end private diff --git a/lib/redmine/codeset_util.rb b/lib/redmine/codeset_util.rb index bb1f972d4..4f056d54a 100644 --- a/lib/redmine/codeset_util.rb +++ b/lib/redmine/codeset_util.rb @@ -6,7 +6,7 @@ module Redmine return str if str.nil? str.force_encoding('UTF-8') if ! str.valid_encoding? - str = str.encode("US-ASCII", :invalid => :replace, + str = str.encode("UTF-16LE", :invalid => :replace, :undef => :replace, :replace => '?').encode("UTF-8") end str @@ -25,11 +25,7 @@ module Redmine str = str.encode("UTF-8", :invalid => :replace, :undef => :replace, :replace => '?') else - str.force_encoding("UTF-8") - if ! str.valid_encoding? - str = str.encode("US-ASCII", :invalid => :replace, - :undef => :replace, :replace => '?').encode("UTF-8") - end + str = replace_invalid_utf8(str) end str end diff --git a/lib/redmine/configuration.rb b/lib/redmine/configuration.rb index 9ab008e2b..c9a9b6b8c 100644 --- a/lib/redmine/configuration.rb +++ b/lib/redmine/configuration.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/core_ext/active_record.rb b/lib/redmine/core_ext/active_record.rb index 6ec901d77..72a794da3 100644 --- a/lib/redmine/core_ext/active_record.rb +++ b/lib/redmine/core_ext/active_record.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/core_ext/date/calculations.rb b/lib/redmine/core_ext/date/calculations.rb index 5ad528f6a..e3a7f60f5 100644 --- a/lib/redmine/core_ext/date/calculations.rb +++ b/lib/redmine/core_ext/date/calculations.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/core_ext/string.rb b/lib/redmine/core_ext/string.rb index c865284ac..2da5ffef9 100644 --- a/lib/redmine/core_ext/string.rb +++ b/lib/redmine/core_ext/string.rb @@ -4,8 +4,4 @@ require File.dirname(__FILE__) + '/string/inflections' class String #:nodoc: include Redmine::CoreExtensions::String::Conversions include Redmine::CoreExtensions::String::Inflections - - def is_binary_data? - ( self.count( "^ -~", "^\r\n" ).fdiv(self.size) > 0.3 || self.index( "\x00" ) ) unless empty? - end end diff --git a/lib/redmine/core_ext/string/conversions.rb b/lib/redmine/core_ext/string/conversions.rb index 0e0e67ebe..fbac0de7b 100644 --- a/lib/redmine/core_ext/string/conversions.rb +++ b/lib/redmine/core_ext/string/conversions.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/core_ext/string/inflections.rb b/lib/redmine/core_ext/string/inflections.rb index ef188e12e..2dd5a1fe5 100644 --- a/lib/redmine/core_ext/string/inflections.rb +++ b/lib/redmine/core_ext/string/inflections.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/database.rb b/lib/redmine/database.rb index b0f43b73c..02580d160 100644 --- a/lib/redmine/database.rb +++ b/lib/redmine/database.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/default_data/loader.rb b/lib/redmine/default_data/loader.rb index 0fdfad61f..f702826c7 100644 --- a/lib/redmine/default_data/loader.rb +++ b/lib/redmine/default_data/loader.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -34,9 +34,10 @@ module Redmine # Loads the default data # Raises a RecordNotSaved exception if something goes wrong - def load(lang=nil) + def load(lang=nil, options={}) raise DataAlreadyLoaded.new("Some configuration data is already loaded.") unless no_data? set_language_if_valid(lang) + workflow = !(options[:workflow] == false) Role.transaction do # Roles @@ -64,12 +65,14 @@ module Redmine :view_calendar, :log_time, :view_time_entries, + :view_news, :comment_news, :view_documents, :view_wiki_pages, :view_wiki_edits, :edit_wiki_pages, :delete_wiki_pages, + :view_messages, :add_messages, :edit_own_messages, :view_files, @@ -89,10 +92,12 @@ module Redmine :view_calendar, :log_time, :view_time_entries, + :view_news, :comment_news, :view_documents, :view_wiki_pages, :view_wiki_edits, + :view_messages, :add_messages, :edit_own_messages, :view_files, @@ -106,10 +111,12 @@ module Redmine :view_gantt, :view_calendar, :view_time_entries, + :view_news, :comment_news, :view_documents, :view_wiki_pages, :view_wiki_edits, + :view_messages, :add_messages, :view_files, :browse_repository, @@ -119,9 +126,11 @@ module Redmine :view_gantt, :view_calendar, :view_time_entries, + :view_news, :view_documents, :view_wiki_pages, :view_wiki_edits, + :view_messages, :view_files, :browse_repository, :view_changesets] @@ -139,31 +148,33 @@ module Redmine Tracker.create!(:name => l(:default_tracker_feature), :default_status_id => new.id, :is_in_chlog => true, :is_in_roadmap => true, :position => 2) Tracker.create!(:name => l(:default_tracker_support), :default_status_id => new.id, :is_in_chlog => false, :is_in_roadmap => false, :position => 3) - # Workflow - Tracker.all.each { |t| - IssueStatus.all.each { |os| - IssueStatus.all.each { |ns| - WorkflowTransition.create!(:tracker_id => t.id, :role_id => manager.id, :old_status_id => os.id, :new_status_id => ns.id) unless os == ns + if workflow + # Workflow + Tracker.all.each { |t| + IssueStatus.all.each { |os| + IssueStatus.all.each { |ns| + WorkflowTransition.create!(:tracker_id => t.id, :role_id => manager.id, :old_status_id => os.id, :new_status_id => ns.id) unless os == ns + } } } - } - Tracker.all.each { |t| - [new, in_progress, resolved, feedback].each { |os| - [in_progress, resolved, feedback, closed].each { |ns| - WorkflowTransition.create!(:tracker_id => t.id, :role_id => developer.id, :old_status_id => os.id, :new_status_id => ns.id) unless os == ns + Tracker.all.each { |t| + [new, in_progress, resolved, feedback].each { |os| + [in_progress, resolved, feedback, closed].each { |ns| + WorkflowTransition.create!(:tracker_id => t.id, :role_id => developer.id, :old_status_id => os.id, :new_status_id => ns.id) unless os == ns + } } } - } - Tracker.all.each { |t| - [new, in_progress, resolved, feedback].each { |os| - [closed].each { |ns| - WorkflowTransition.create!(:tracker_id => t.id, :role_id => reporter.id, :old_status_id => os.id, :new_status_id => ns.id) unless os == ns + Tracker.all.each { |t| + [new, in_progress, resolved, feedback].each { |os| + [closed].each { |ns| + WorkflowTransition.create!(:tracker_id => t.id, :role_id => reporter.id, :old_status_id => os.id, :new_status_id => ns.id) unless os == ns + } } + WorkflowTransition.create!(:tracker_id => t.id, :role_id => reporter.id, :old_status_id => resolved.id, :new_status_id => feedback.id) } - WorkflowTransition.create!(:tracker_id => t.id, :role_id => reporter.id, :old_status_id => resolved.id, :new_status_id => feedback.id) - } + end # Enumerations IssuePriority.create!(:name => l(:default_priority_low), :position => 1) diff --git a/lib/redmine/export/csv.rb b/lib/redmine/export/csv.rb index 8b64ad60d..dfbe36f4f 100644 --- a/lib/redmine/export/csv.rb +++ b/lib/redmine/export/csv.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/export/pdf.rb b/lib/redmine/export/pdf.rb index fd79c374a..ff383d108 100644 --- a/lib/redmine/export/pdf.rb +++ b/lib/redmine/export/pdf.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/export/pdf/issues_pdf_helper.rb b/lib/redmine/export/pdf/issues_pdf_helper.rb index 75f63d272..a6db6931f 100644 --- a/lib/redmine/export/pdf/issues_pdf_helper.rb +++ b/lib/redmine/export/pdf/issues_pdf_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -67,9 +67,10 @@ module Redmine while right.size < rows right << nil end - - half = (issue.visible_custom_field_values.size / 2.0).ceil - issue.visible_custom_field_values.each_with_index do |custom_value, i| + + custom_field_values = issue.visible_custom_field_values.reject {|value| value.custom_field.full_width_layout?} + half = (custom_field_values.size / 2.0).ceil + custom_field_values.each_with_index do |custom_value, i| (i < half ? left : right) << [custom_value.custom_field.name, show_value(custom_value, false)] end @@ -128,7 +129,18 @@ module Redmine :inline_attachments => false ) pdf.RDMwriteFormattedCell(35+155, 5, '', '', text, issue.attachments, "LRB") - + + custom_field_values = issue.visible_custom_field_values.select {|value| value.custom_field.full_width_layout?} + custom_field_values.each do |value| + text = show_value(value, false) + next if text.blank? + + pdf.SetFontStyle('B',9) + pdf.RDMCell(35+155, 5, value.custom_field.name, "LRT", 1) + pdf.SetFontStyle('',9) + pdf.RDMwriteHTMLCell(35+155, 5, '', '', text, issue.attachments, "LRB") + end + unless issue.leaf? truncate_length = (!is_cjk? ? 90 : 65) pdf.SetFontStyle('B',9) @@ -266,8 +278,8 @@ module Redmine table_width = col_width.inject(0, :+) end - # use full width if the description is displayed - if table_width > 0 && query.has_column?(:description) + # use full width if the description or last_notes are displayed + if table_width > 0 && (query.has_column?(:description) || query.has_column?(:last_notes)) col_width = col_width.map {|w| w * (page_width - right_margin - left_margin) / table_width} table_width = col_width.inject(0, :+) end @@ -287,12 +299,14 @@ module Redmine totals_by_group = query.totals_by_group render_table_header(pdf, query, col_width, row_height, table_width) previous_group = false + result_count_by_group = query.result_count_by_group + issue_list(issues) do |issue, level| if query.grouped? && (group = query.group_by_column.value(issue)) != previous_group pdf.SetFontStyle('B',10) group_label = group.blank? ? 'None' : group.to_s.dup - group_label << " (#{query.issue_count_by_group[group]})" + group_label << " (#{result_count_by_group[group]})" pdf.bookmark group_label, 0, -1 pdf.RDMCell(table_width, row_height * 2, group_label, 'LR', 1, 'L') pdf.SetFontStyle('',8) @@ -327,6 +341,13 @@ module Redmine pdf.RDMwriteHTMLCell(0, 5, 10, '', issue.description.to_s, issue.attachments, "LRBT") pdf.set_auto_page_break(false) end + + if query.has_column?(:last_notes) && issue.last_notes.present? + pdf.set_x(10) + pdf.set_auto_page_break(true, bottom_margin) + pdf.RDMwriteHTMLCell(0, 5, 10, '', issue.last_notes.to_s, [], "LRBT") + pdf.set_auto_page_break(false) + end end if issues.size == Setting.issues_export_limit.to_i @@ -353,8 +374,11 @@ module Redmine show_value(cv, false) else value = issue.send(column.name) - if column.name == :subject + case column.name + when :subject value = " " * level + value + when :attachments + value = value.to_a.map {|a| a.filename}.join("\n") end if value.is_a?(Date) format_date(value) diff --git a/lib/redmine/export/pdf/wiki_pdf_helper.rb b/lib/redmine/export/pdf/wiki_pdf_helper.rb index aea7b7e44..206820e54 100644 --- a/lib/redmine/export/pdf/wiki_pdf_helper.rb +++ b/lib/redmine/export/pdf/wiki_pdf_helper.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/field_format.rb b/lib/redmine/field_format.rb index bdc0d1a98..e4bad2863 100644 --- a/lib/redmine/field_format.rb +++ b/lib/redmine/field_format.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -47,6 +47,13 @@ module Redmine formats.map {|format| [::I18n.t(format.label), format.name] }.sort_by(&:first) end + # Returns an array of formats that can be used for a custom field class + def self.formats_for_custom_field_class(klass=nil) + all.values.select do |format| + format.class.customized_class_names.nil? || format.class.customized_class_names.include?(klass.name) + end + end + class Base include Singleton include Redmine::I18n @@ -60,6 +67,10 @@ module Redmine class_attribute :multiple_supported self.multiple_supported = false + # Set this to true if the format supports filtering on custom values + class_attribute :is_filter_supported + self.is_filter_supported = true + # Set this to true if the format supports textual search on custom values class_attribute :searchable_supported self.searchable_supported = false @@ -68,6 +79,10 @@ module Redmine class_attribute :totalable_supported self.totalable_supported = false + # Set this to false if field cannot be bulk edited + class_attribute :bulk_edit_supported + self.bulk_edit_supported = true + # Restricts the classes that the custom field can be added to # Set to nil for no restrictions class_attribute :customized_class_names @@ -80,6 +95,9 @@ module Redmine class_attribute :change_as_diff self.change_as_diff = false + class_attribute :change_no_details + self.change_no_details = false + def self.add(name) self.format_name = name Redmine::FieldFormat.add(name, self) @@ -90,7 +108,7 @@ module Redmine CustomField.store_accessor :format_store, *args end - field_attributes :url_pattern + field_attributes :url_pattern, :full_width_layout def name self.class.format_name @@ -100,6 +118,19 @@ module Redmine "label_#{name}" end + def set_custom_field_value(custom_field, custom_field_value, value) + if value.is_a?(Array) + value = value.map(&:to_s).reject{|v| v==''}.uniq + if value.empty? + value << '' + end + else + value = value.to_s + end + + value + end + def cast_custom_value(custom_value) cast_value(custom_value.custom_field, custom_value.value, custom_value.customized) end @@ -136,12 +167,13 @@ module Redmine def value_from_keyword(custom_field, keyword, object) possible_values_options = possible_values_options(custom_field, object) if possible_values_options.present? - keyword = keyword.to_s - if v = possible_values_options.detect {|text, id| keyword.casecmp(text) == 0} - if v.is_a?(Array) - v.last - else - v + parse_keyword(custom_field, keyword) do |k| + if v = possible_values_options.detect {|text, id| k.casecmp(text) == 0} + if v.is_a?(Array) + v.last + else + v + end end end else @@ -149,6 +181,31 @@ module Redmine end end + def parse_keyword(custom_field, keyword, &block) + separator = Regexp.escape "," + keyword = keyword.to_s + + if custom_field.multiple? + values = [] + while keyword.length > 0 + k = keyword.dup + loop do + if value = yield(k.strip) + values << value + break + elsif k.slice!(/#{separator}([^#{separator}]*)\Z/).nil? + break + end + end + keyword.slice!(/\A#{Regexp.escape k}#{separator}?/) + end + values + else + yield keyword.strip + end + end + protected :parse_keyword + # Returns the validation errors for custom_field # Should return an empty array if custom_field is valid def validate_custom_field(custom_field) @@ -162,6 +219,7 @@ module Redmine # Returns the validation error messages for custom_value # Should return an empty array if custom_value is valid + # custom_value is a CustomFieldValue. def validate_custom_value(custom_value) values = Array.wrap(custom_value.value).reject {|value| value.to_s == ''} errors = values.map do |value| @@ -174,6 +232,10 @@ module Redmine [] end + # CustomValue after_save callback + def after_save_custom_value(custom_field, custom_value) + end + def formatted_custom_value(view, custom_value, html=false) formatted_value(view, custom_value.custom_field, custom_value.value, custom_value.customized, html) end @@ -350,11 +412,11 @@ module Redmine end def edit_tag(view, tag_id, tag_name, custom_value, options={}) - view.text_area_tag(tag_name, custom_value.value, options.merge(:id => tag_id, :rows => 3)) + view.text_area_tag(tag_name, custom_value.value, options.merge(:id => tag_id, :rows => 8)) end def bulk_edit_tag(view, tag_id, tag_name, custom_field, objects, value, options={}) - view.text_area_tag(tag_name, value, options.merge(:id => tag_id, :rows => 3)) + + view.text_area_tag(tag_name, value, options.merge(:id => tag_id, :rows => 8)) + '
    '.html_safe + bulk_clear_tag(view, tag_id, tag_name, custom_field, value) end @@ -516,7 +578,7 @@ module Redmine end def query_filter_options(custom_field, query) - {:type => :list_optional, :values => query_filter_values(custom_field, query)} + {:type => :list_optional, :values => lambda { query_filter_values(custom_field, query) }} end protected @@ -674,6 +736,16 @@ module Redmine options 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) if target_class.respond_to?(:fields_for_order_statement) target_class.fields_for_order_statement(value_join_alias(custom_field)) @@ -728,8 +800,9 @@ module Redmine end def value_from_keyword(custom_field, keyword, object) - value = custom_field.enumerations.where("LOWER(name) LIKE LOWER(?)", keyword).first - value ? value.id : nil + parse_keyword(custom_field, keyword) do |k| + custom_field.enumerations.where("LOWER(name) LIKE LOWER(?)", k).first.try(:id) + end end end @@ -762,8 +835,9 @@ module Redmine def value_from_keyword(custom_field, keyword, object) users = possible_values_records(custom_field, object).to_a - user = Principal.detect_by_keyword(users, keyword) - user ? user.id : nil + parse_keyword(custom_field, keyword) do |k| + Principal.detect_by_keyword(users, k).try(:id) + end end def before_custom_field_save(custom_field) @@ -772,6 +846,10 @@ module Redmine custom_field.user_role.map!(&:to_s).reject!(&:blank?) end end + + def query_filter_values(custom_field, query) + query.author_values + end end class VersionFormat < RecordList @@ -780,7 +858,7 @@ module Redmine field_attributes :version_status def possible_values_options(custom_field, object=nil) - versions_options(custom_field, object) + possible_values_records(custom_field, object).sort.collect{|v| [v.to_s, v.id.to_s] } end def before_custom_field_save(custom_field) @@ -793,18 +871,19 @@ module Redmine protected def query_filter_values(custom_field, query) - versions_options(custom_field, query.project, true) + versions = possible_values_records(custom_field, query.project, true) + Version.sort_by_status(versions).collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s, l("version_status_#{s.status}")] } end - def versions_options(custom_field, object, all_statuses=false) + def possible_values_records(custom_field, object=nil, all_statuses=false) if object.is_a?(Array) projects = object.map {|o| o.respond_to?(:project) ? o.project : nil}.compact.uniq - projects.map {|project| possible_values_options(custom_field, project)}.reduce(:&) || [] + projects.map {|project| possible_values_records(custom_field, project)}.reduce(:&) || [] elsif object.respond_to?(:project) && object.project scope = object.project.shared_versions filtered_versions_options(custom_field, scope, all_statuses) elsif object.nil? - scope = Version.visible.where(:sharing => 'system') + scope = ::Version.visible.where(:sharing => 'system') filtered_versions_options(custom_field, scope, all_statuses) else [] @@ -818,7 +897,123 @@ module Redmine scope = scope.where(:status => statuses.map(&:to_s)) end end - scope.sort.collect{|u| [u.to_s, u.id.to_s] } + scope + end + end + + class AttachmentFormat < Base + add 'attachment' + self.form_partial = 'custom_fields/formats/attachment' + self.is_filter_supported = false + self.change_no_details = true + self.bulk_edit_supported = false + field_attributes :extensions_allowed + + def set_custom_field_value(custom_field, custom_field_value, value) + attachment_present = false + + if value.is_a?(Hash) + attachment_present = true + value = value.except(:blank) + + if value.values.any? && value.values.all? {|v| v.is_a?(Hash)} + value = value.values.first + end + + if value.key?(:id) + value = set_custom_field_value_by_id(custom_field, custom_field_value, value[:id]) + elsif value[:token].present? + if attachment = Attachment.find_by_token(value[:token]) + value = attachment.id.to_s + else + value = '' + end + elsif value.key?(:file) + attachment = Attachment.new(:file => value[:file], :author => User.current) + if attachment.save + value = attachment.id.to_s + else + value = '' + end + else + attachment_present = false + value = '' + end + elsif value.is_a?(String) + value = set_custom_field_value_by_id(custom_field, custom_field_value, value) + end + custom_field_value.instance_variable_set "@attachment_present", attachment_present + + value + end + + def set_custom_field_value_by_id(custom_field, custom_field_value, id) + attachment = Attachment.find_by_id(id) + if attachment && attachment.container.is_a?(CustomValue) && attachment.container.customized == custom_field_value.customized + id.to_s + else + '' + end + end + private :set_custom_field_value_by_id + + def cast_single_value(custom_field, value, customized=nil) + Attachment.find_by_id(value.to_i) if value.present? && value.respond_to?(:to_i) + end + + def validate_custom_value(custom_value) + errors = [] + + if custom_value.value.blank? + if custom_value.instance_variable_get("@attachment_present") + errors << ::I18n.t('activerecord.errors.messages.invalid') + end + else + if custom_value.value.present? + attachment = Attachment.where(:id => custom_value.value.to_s).first + extensions = custom_value.custom_field.extensions_allowed + if attachment && extensions.present? && !attachment.extension_in?(extensions) + errors << "#{::I18n.t('activerecord.errors.messages.invalid')} (#{l(:setting_attachment_extensions_allowed)}: #{extensions})" + end + end + end + + errors.uniq + end + + def after_save_custom_value(custom_field, custom_value) + if custom_value.value_changed? + if custom_value.value.present? + attachment = Attachment.where(:id => custom_value.value.to_s).first + if attachment + attachment.container = custom_value + attachment.save! + end + end + if custom_value.value_was.present? + attachment = Attachment.where(:id => custom_value.value_was.to_s).first + if attachment + attachment.destroy + end + end + end + end + + def edit_tag(view, tag_id, tag_name, custom_value, options={}) + attachment = nil + if custom_value.value.present? + attachment = Attachment.find_by_id(custom_value.value) + end + + view.hidden_field_tag("#{tag_name}[blank]", "") + + view.render(:partial => 'attachments/form', + :locals => { + :attachment_param => tag_name, + :multiple => false, + :description => false, + :saved_attachments => [attachment].compact, + :filedrop => false + }) end end end diff --git a/lib/redmine/helpers/calendar.rb b/lib/redmine/helpers/calendar.rb index 0e2109768..3641097ba 100644 --- a/lib/redmine/helpers/calendar.rb +++ b/lib/redmine/helpers/calendar.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/helpers/diff.rb b/lib/redmine/helpers/diff.rb index a6d81620a..d62e763c6 100644 --- a/lib/redmine/helpers/diff.rb +++ b/lib/redmine/helpers/diff.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/helpers/gantt.rb b/lib/redmine/helpers/gantt.rb index 87e614a00..a4b7a2f39 100644 --- a/lib/redmine/helpers/gantt.rb +++ b/lib/redmine/helpers/gantt.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -168,7 +168,7 @@ module Redmine joins("LEFT JOIN #{Project.table_name} child ON #{Project.table_name}.lft <= child.lft AND #{Project.table_name}.rgt >= child.rgt"). where("child.id IN (?)", ids). order("#{Project.table_name}.lft ASC"). - uniq. + distinct. to_a else @projects = [] @@ -314,7 +314,10 @@ module Redmine def line_for_issue(issue, options) # Skip issues that don't have a due_before (due_date or version's due_date) if issue.is_a?(Issue) && issue.due_before - label = "#{issue.status.name} #{issue.done_ratio}%" + label = issue.status.name.dup + unless issue.disabled_core_fields.include?('done_ratio') + label << " #{issue.done_ratio}%" + end markers = !issue.leaf? line(issue.start_date, issue.due_before, issue.done_ratio, markers, label, options, issue) end diff --git a/lib/redmine/helpers/time_report.rb b/lib/redmine/helpers/time_report.rb index 8991592af..fb6384cb6 100644 --- a/lib/redmine/helpers/time_report.rb +++ b/lib/redmine/helpers/time_report.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -45,7 +45,8 @@ module Redmine unless @criteria.empty? time_columns = %w(tyear tmonth tweek spent_on) @hours = [] - @scope.includes(:issue, :activity). + @scope.includes(:activity). + reorder(nil). group(@criteria.collect{|criteria| @available_criteria[criteria][:sql]} + time_columns). joins(@criteria.collect{|criteria| @available_criteria[criteria][:joins]}.compact). sum(:hours).each do |hash, hours| diff --git a/lib/redmine/helpers/url.rb b/lib/redmine/helpers/url.rb index 0cc149c3b..9dd8a7114 100644 --- a/lib/redmine/helpers/url.rb +++ b/lib/redmine/helpers/url.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/hook.rb b/lib/redmine/hook.rb index fb68106d4..3d273b474 100644 --- a/lib/redmine/hook.rb +++ b/lib/redmine/hook.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/hook/listener.rb b/lib/redmine/hook/listener.rb index 6494a7b5a..a84636fdf 100644 --- a/lib/redmine/hook/listener.rb +++ b/lib/redmine/hook/listener.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/hook/view_listener.rb b/lib/redmine/hook/view_listener.rb index f493578d2..cb4c578a1 100644 --- a/lib/redmine/hook/view_listener.rb +++ b/lib/redmine/hook/view_listener.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/i18n.rb b/lib/redmine/i18n.rb index 7b4495e0e..43b74bb26 100644 --- a/lib/redmine/i18n.rb +++ b/lib/redmine/i18n.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -45,11 +45,11 @@ module Redmine def l_hours(hours) hours = hours.to_f - l((hours < 2.0 ? :label_f_hour : :label_f_hour_plural), :value => ("%.2f" % hours.to_f)) + l((hours < 2.0 ? :label_f_hour : :label_f_hour_plural), :value => format_hours(hours)) end def l_hours_short(hours) - l(:label_f_hour_short, :value => ("%.2f" % hours.to_f)) + l(:label_f_hour_short, :value => format_hours(hours.to_f)) end def ll(lang, str, arg=nil) @@ -82,6 +82,18 @@ module Redmine (include_date ? "#{format_date(local)} " : "") + ::I18n.l(local, options) end + def format_hours(hours) + return "" if hours.blank? + + if Setting.timespan_format == 'minutes' + h = hours.floor + m = ((hours - h) * 60).round + "%d:%02d" % [ h, m ] + else + "%.2f" % hours.to_f + end + end + def day_name(day) ::I18n.t('date.day_names')[day % 7] end @@ -118,7 +130,7 @@ module Redmine end def find_language(lang) - @@languages_lookup = valid_languages.inject({}) {|k, v| k[v.to_s.downcase] = v; k } + @@languages_lookup ||= valid_languages.inject({}) {|k, v| k[v.to_s.downcase] = v; k } @@languages_lookup[lang.to_s.downcase] end diff --git a/lib/redmine/imap.rb b/lib/redmine/imap.rb index c04bb3d07..658b8d1bf 100644 --- a/lib/redmine/imap.rb +++ b/lib/redmine/imap.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/menu_manager.rb b/lib/redmine/menu_manager.rb index 3d19edce7..fb34e70c5 100644 --- a/lib/redmine/menu_manager.rb +++ b/lib/redmine/menu_manager.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -22,6 +22,9 @@ module Redmine module MenuController def self.included(base) + base.class_attribute :main_menu + base.main_menu = true + base.extend(ClassMethods) end @@ -51,18 +54,35 @@ module Redmine self.class.menu_items end + def current_menu(project) + if project && !project.new_record? + :project_menu + elsif self.class.main_menu + :application_menu + end + end + # Returns the menu item name according to the current action def current_menu_item @current_menu_item ||= menu_items[controller_name.to_sym][:actions][action_name.to_sym] || menu_items[controller_name.to_sym][:default] end + # Redirects user to the menu item + # Returns false if user is not authorized + def redirect_to_menu_item(name) + redirect_to_project_menu_item(nil, name) + end + # Redirects user to the menu item of the given project # Returns false if user is not authorized def redirect_to_project_menu_item(project, name) - item = Redmine::MenuManager.items(:project_menu).detect {|i| i.name.to_s == name.to_s} + menu = project.nil? ? :application_menu : :project_menu + item = Redmine::MenuManager.items(menu).detect {|i| i.name.to_s == name.to_s} if item && item.allowed?(User.current, project) - redirect_to({item.param => project}.merge(item.url)) + url = item.url + url = {item.param => project}.merge(url) if project + redirect_to url return true end false @@ -77,12 +97,14 @@ module Redmine # Renders the application main menu def render_main_menu(project) - render_menu((project && !project.new_record?) ? :project_menu : :application_menu, project) + if menu_name = controller.current_menu(project) + render_menu(menu_name, project) + end end def display_main_menu?(project) - menu_name = project && !project.new_record? ? :project_menu : :application_menu - Redmine::MenuManager.items(menu_name).children.present? + menu_name = controller.current_menu(project) + menu_name.present? && Redmine::MenuManager.items(menu_name).children.present? end def render_menu(menu, project=nil) diff --git a/lib/redmine/mime_type.rb b/lib/redmine/mime_type.rb index 2a3db8080..45e352af8 100644 --- a/lib/redmine/mime_type.rb +++ b/lib/redmine/mime_type.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -53,6 +53,11 @@ module Redmine map end + # returns all full mime types for a given (top level) type + def self.by_type(type) + MIME_TYPES.keys.select{|m| m.start_with? "#{type}/"} + end + # returns mime type for name or nil if unknown def self.of(name) return nil unless name.present? diff --git a/lib/redmine/my_page.rb b/lib/redmine/my_page.rb new file mode 100644 index 000000000..1bbefe493 --- /dev/null +++ b/lib/redmine/my_page.rb @@ -0,0 +1,91 @@ +# Redmine - project management software +# Copyright (C) 2006-2017 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +module Redmine + module MyPage + include Redmine::I18n + + CORE_GROUPS = ['top', 'left', 'right'] + + CORE_BLOCKS = { + 'issuesassignedtome' => {:label => :label_assigned_to_me_issues}, + 'issuesreportedbyme' => {:label => :label_reported_issues}, + 'issueswatched' => {:label => :label_watched_issues}, + 'issuequery' => {:label => :label_issue_plural, :max_occurs => 3}, + 'news' => {:label => :label_news_latest}, + 'calendar' => {:label => :label_calendar}, + 'documents' => {:label => :label_document_plural}, + 'timelog' => {:label => :label_spent_time} + } + + def self.groups + CORE_GROUPS.dup.freeze + end + + # Returns the available blocks + def self.blocks + CORE_BLOCKS.merge(additional_blocks).freeze + end + + def self.block_options(blocks_in_use=[]) + options = [] + blocks.each do |block, block_options| + indexes = blocks_in_use.map {|n| + if n =~ /\A#{block}(__(\d+))?\z/ + $2.to_i + end + }.compact + + occurs = indexes.size + block_id = indexes.any? ? "#{block}__#{indexes.max + 1}" : block + disabled = (occurs >= (Redmine::MyPage.blocks[block][:max_occurs] || 1)) + block_id = nil if disabled + + label = block_options[:label] + options << [l("my.blocks.#{label}", :default => [label, label.to_s.humanize]), block_id] + end + options + end + + def self.valid_block?(block, blocks_in_use=[]) + block.present? && block_options(blocks_in_use).map(&:last).include?(block) + end + + def self.find_block(block) + block.to_s =~ /\A(.*?)(__\d+)?\z/ + name = $1 + blocks.has_key?(name) ? blocks[name].merge(:name => name) : nil + end + + # Returns the additional blocks that are defined by plugin partials + def self.additional_blocks + @@additional_blocks ||= Dir.glob("#{Redmine::Plugin.directory}/*/app/views/my/blocks/_*.{rhtml,erb}").inject({}) do |h,file| + name = File.basename(file).split('.').first.gsub(/^_/, '') + h[name] = {:label => name.to_sym, :partial => "my/blocks/#{name}"} + h + end + end + + # Returns the default layout for My Page + def self.default_layout + { + 'left' => ['issuesassignedtome'], + 'right' => ['issuesreportedbyme'] + } + end + end +end diff --git a/lib/redmine/nested_set/issue_nested_set.rb b/lib/redmine/nested_set/issue_nested_set.rb index 932f56351..bb921d6de 100644 --- a/lib/redmine/nested_set/issue_nested_set.rb +++ b/lib/redmine/nested_set/issue_nested_set.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -176,13 +176,23 @@ module Redmine reorder(:id).lock.ids update_all(:root_id => nil, :lft => nil, :rgt => nil) where(:parent_id => nil).update_all(["root_id = id, lft = ?, rgt = ?", 1, 2]) - roots_with_children = joins("JOIN #{table_name} parent ON parent.id = #{table_name}.parent_id AND parent.id = parent.root_id").uniq.pluck("parent.id") + roots_with_children = joins("JOIN #{table_name} parent ON parent.id = #{table_name}.parent_id AND parent.id = parent.root_id").distinct.pluck("parent.id") roots_with_children.each do |root_id| rebuild_nodes(root_id) end end end + def rebuild_single_tree!(root_id) + root = Issue.where(:parent_id => nil).find(root_id) + transaction do + where(root_id: root_id).reorder(:id).lock.ids + where(root_id: root_id).update_all(:lft => nil, :rgt => nil) + where(root_id: root_id, parent_id: nil).update_all(["lft = ?, rgt = ?", 1, 2]) + rebuild_nodes(root_id) + end + end + private def rebuild_nodes(parent_id = nil) diff --git a/lib/redmine/nested_set/project_nested_set.rb b/lib/redmine/nested_set/project_nested_set.rb index fd6408131..d6d33efaf 100644 --- a/lib/redmine/nested_set/project_nested_set.rb +++ b/lib/redmine/nested_set/project_nested_set.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/nested_set/traversing.rb b/lib/redmine/nested_set/traversing.rb index 67b13d779..07b6deec4 100644 --- a/lib/redmine/nested_set/traversing.rb +++ b/lib/redmine/nested_set/traversing.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/pagination.rb b/lib/redmine/pagination.rb index e0629981c..0131c86e4 100644 --- a/lib/redmine/pagination.rb +++ b/lib/redmine/pagination.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -116,16 +116,6 @@ module Redmine # def paginate(scope, options={}) options = options.dup - finder_options = options.extract!( - :conditions, - :order, - :joins, - :include, - :select - ) - if scope.is_a?(Symbol) || finder_options.values.compact.any? - return deprecated_paginate(scope, finder_options, options) - end paginator = paginator(scope.count, options) collection = scope.limit(paginator.per_page).offset(paginator.offset).to_a @@ -133,13 +123,6 @@ module Redmine return paginator, collection end - def deprecated_paginate(arg, finder_options, options={}) - ActiveSupport::Deprecation.warn "#paginate with a Symbol and/or find options is depreceted and will be removed. Use a scope instead." - klass = arg.is_a?(Symbol) ? arg.to_s.classify.constantize : arg - scope = klass.scoped(finder_options) - paginate(scope, options) - end - def paginator(item_count, options={}) options.assert_valid_keys :parameter, :per_page @@ -162,7 +145,7 @@ module Redmine if block_given? yield text, parameters, options else - link_to text, params.merge(parameters), options + link_to text, {:params => request.query_parameters.merge(parameters)}, options end end end diff --git a/lib/redmine/platform.rb b/lib/redmine/platform.rb index 8a86382ab..5fdd58d7c 100644 --- a/lib/redmine/platform.rb +++ b/lib/redmine/platform.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/plugin.rb b/lib/redmine/plugin.rb index 503c3db2c..c3e8c5b30 100644 --- a/lib/redmine/plugin.rb +++ b/lib/redmine/plugin.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/pop3.rb b/lib/redmine/pop3.rb index f4afe2e95..548dfc173 100644 --- a/lib/redmine/pop3.rb +++ b/lib/redmine/pop3.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/safe_attributes.rb b/lib/redmine/safe_attributes.rb index 363048d29..0bde0f970 100644 --- a/lib/redmine/safe_attributes.rb +++ b/lib/redmine/safe_attributes.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -74,7 +74,7 @@ module Redmine # # => {'title' => 'My book'} def delete_unsafe_attributes(attrs, user=User.current) safe = safe_attribute_names(user) - attrs.dup.delete_if {|k,v| !safe.include?(k)} + attrs.dup.delete_if {|k,v| !safe.include?(k.to_s)} end # Sets attributes from attrs that are safe diff --git a/lib/redmine/scm/adapters.rb b/lib/redmine/scm/adapters.rb index 6c65044c1..bee5a5398 100644 --- a/lib/redmine/scm/adapters.rb +++ b/lib/redmine/scm/adapters.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -18,8 +18,6 @@ module Redmine module Scm module Adapters - class CommandFailed < StandardError #:nodoc: - end end end end diff --git a/lib/redmine/scm/adapters/abstract_adapter.rb b/lib/redmine/scm/adapters/abstract_adapter.rb index 9ed8b25db..437ba92a8 100644 --- a/lib/redmine/scm/adapters/abstract_adapter.rb +++ b/lib/redmine/scm/adapters/abstract_adapter.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -22,21 +22,23 @@ module Redmine module Scm module Adapters class AbstractAdapter #:nodoc: + include Redmine::Utils::Shell # raised if scm command exited with error, e.g. unknown revision. class ScmCommandAborted < ::Redmine::Scm::Adapters::CommandFailed; end class << self + def client_command "" end + def shell_quote(str) + Redmine::Utils::Shell.shell_quote str + end + def shell_quote_command - if Redmine::Platform.mswin? && RUBY_PLATFORM == 'java' - client_command - else - shell_quote(client_command) - end + Redmine::Utils::Shell.shell_quote_command client_command end # Returns the version of the scm client @@ -64,13 +66,6 @@ module Redmine true end - def shell_quote(str) - if Redmine::Platform.mswin? - '"' + str.gsub(/"/, '\\"') + '"' - else - "'" + str.gsub(/'/, "'\"'\"'") + "'" - end - end end def initialize(url, root_url=nil, login=nil, password=nil, @@ -180,10 +175,6 @@ module Redmine (path[-1,1] == "/") ? path[0..-2] : path end - def shell_quote(str) - self.class.shell_quote(str) - end - private def retrieve_root_url info = self.info @@ -430,6 +421,14 @@ module Redmine class Branch < String attr_accessor :revision, :scmid end + + module ScmData + def self.binary?(data) + unless data.empty? + data.count( "^ -~", "^\r\n" ).fdiv(data.size) > 0.3 || data.index( "\x00" ) + end + end + end end end end diff --git a/lib/redmine/scm/adapters/bazaar_adapter.rb b/lib/redmine/scm/adapters/bazaar_adapter.rb index 1290292c9..dc293d24c 100644 --- a/lib/redmine/scm/adapters/bazaar_adapter.rb +++ b/lib/redmine/scm/adapters/bazaar_adapter.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/views/my_page/block.rb b/lib/redmine/scm/adapters/command_failed.rb similarity index 65% rename from lib/redmine/views/my_page/block.rb rename to lib/redmine/scm/adapters/command_failed.rb index 07398d788..562b00658 100644 --- a/lib/redmine/views/my_page/block.rb +++ b/lib/redmine/scm/adapters/command_failed.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,16 +16,9 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. module Redmine - module Views - module MyPage - module Block - def self.additional_blocks - @@additional_blocks ||= Dir.glob("#{Redmine::Plugin.directory}/*/app/views/my/blocks/_*.{rhtml,erb}").inject({}) do |h,file| - name = File.basename(file).split('.').first.gsub(/^_/, '') - h[name] = name.to_sym - h - end - end + module Scm + module Adapters + class CommandFailed < StandardError #:nodoc: end end end diff --git a/lib/redmine/scm/adapters/darcs_adapter.rb b/lib/redmine/scm/adapters/darcs_adapter.rb index 415e6b097..dd0f4f5d3 100644 --- a/lib/redmine/scm/adapters/darcs_adapter.rb +++ b/lib/redmine/scm/adapters/darcs_adapter.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/scm/adapters/filesystem_adapter.rb b/lib/redmine/scm/adapters/filesystem_adapter.rb index b7c559e11..1373a93c0 100644 --- a/lib/redmine/scm/adapters/filesystem_adapter.rb +++ b/lib/redmine/scm/adapters/filesystem_adapter.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # FileSystem adapter # File written by Paul Rivier, at Demotera. diff --git a/lib/redmine/scm/adapters/git_adapter.rb b/lib/redmine/scm/adapters/git_adapter.rb index 3df6a9062..fcc77f393 100644 --- a/lib/redmine/scm/adapters/git_adapter.rb +++ b/lib/redmine/scm/adapters/git_adapter.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -338,7 +338,7 @@ module Redmine content = nil git_cmd(cmd_args) { |io| io.binmode; content = io.read } # git annotates binary files - return nil if content.is_binary_data? + return nil if ScmData.binary?(content) identifier = '' # git shows commit author on the first occurrence only authors_by_commit = {} diff --git a/lib/redmine/scm/adapters/mercurial_adapter.rb b/lib/redmine/scm/adapters/mercurial_adapter.rb index 12724bb09..599c3a42f 100644 --- a/lib/redmine/scm/adapters/mercurial_adapter.rb +++ b/lib/redmine/scm/adapters/mercurial_adapter.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/scm/adapters/subversion_adapter.rb b/lib/redmine/scm/adapters/subversion_adapter.rb index 508a2e566..318f045a0 100644 --- a/lib/redmine/scm/adapters/subversion_adapter.rb +++ b/lib/redmine/scm/adapters/subversion_adapter.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/search.rb b/lib/redmine/search.rb index ecc6bb652..dce36a7ba 100644 --- a/lib/redmine/search.rb +++ b/lib/redmine/search.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/sort_criteria.rb b/lib/redmine/sort_criteria.rb new file mode 100644 index 000000000..d8fa3ee3d --- /dev/null +++ b/lib/redmine/sort_criteria.rb @@ -0,0 +1,105 @@ +# Redmine - project management software +# Copyright (C) 2006-2017 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +module Redmine + class SortCriteria < Array + def initialize(arg=nil) + super() + if arg.is_a?(Array) + replace arg + elsif arg.is_a?(String) + replace arg.split(',').collect {|s| s.split(':')[0..1]} + elsif arg.respond_to?(:values) + replace arg.values + elsif arg + raise ArgumentError.new("SortCriteria#new takes an Array, String or Hash, not a #{arg.class.name}.") + end + normalize! + end + + def to_param + self.collect {|k,o| k + (o == 'desc' ? ':desc' : '')}.join(',') + end + + def to_a + Array.new(self) + end + + def add!(key, asc) + key = key.to_s + delete_if {|k,o| k == key} + prepend([key, asc]) + normalize! + end + + def add(*args) + self.class.new(self).add!(*args) + end + + def first_key + first.try(:first) + end + + def first_asc? + first.try(:last) == 'asc' + end + + def key_at(arg) + self[arg].try(:first) + end + + def order_at(arg) + self[arg].try(:last) + end + + def order_for(key) + detect {|k, order| key.to_s == k}.try(:last) + end + + def sort_clause(sortable_columns) + if sortable_columns.is_a?(Array) + sortable_columns = sortable_columns.inject({}) {|h,k| h[k]=k; h} + end + + sql = self.collect do |k,o| + if s = sortable_columns[k] + s = [s] unless s.is_a?(Array) + s.collect {|c| append_order(c, o)} + end + end.flatten.compact + sql.blank? ? nil : sql + end + + private + + 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.slice!(3) + self + end + + # Appends ASC/DESC to the sort criterion unless it has a fixed order + def append_order(criterion, order) + if criterion =~ / (asc|desc)$/i + criterion + else + "#{criterion} #{order.to_s.upcase}" + end + end + end +end diff --git a/lib/redmine/subclass_factory.rb b/lib/redmine/subclass_factory.rb index ed331132f..c1936b2c1 100644 --- a/lib/redmine/subclass_factory.rb +++ b/lib/redmine/subclass_factory.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/sudo_mode.rb b/lib/redmine/sudo_mode.rb index a016c9821..eddbc04ce 100644 --- a/lib/redmine/sudo_mode.rb +++ b/lib/redmine/sudo_mode.rb @@ -46,7 +46,7 @@ module Redmine extend ActiveSupport::Concern included do - around_filter :sudo_mode + around_action :sudo_mode end # Sudo mode Around Filter @@ -108,7 +108,7 @@ module Redmine @sudo_form ||= SudoMode::Form.new @sudo_form.original_fields = params.slice( *param_names ) # a simple 'render "sudo_mode/new"' works when used directly inside an - # action, but not when called from a before_filter: + # action, but not when called from a before_action: respond_to do |format| format.html { render 'sudo_mode/new' } format.js { render 'sudo_mode/new' } @@ -168,7 +168,7 @@ module Redmine actions = args.dup options = actions.extract_options! filter = SudoRequestFilter.new Array(options[:parameters]), Array(options[:only]) - before_filter filter, only: actions + before_action filter, only: actions end end end @@ -183,7 +183,7 @@ module Redmine # # Calling this method also turns was_used? to true, therefore # it is important to only call this when sudo is actually needed, as the last - # condition to determine wether a change can be done or not. + # condition to determine whether a change can be done or not. # # If you do it wrong, timeout of the sudo mode will happen too late or not at # all. diff --git a/lib/redmine/syntax_highlighting.rb b/lib/redmine/syntax_highlighting.rb index feff4f6b7..49d9f4110 100644 --- a/lib/redmine/syntax_highlighting.rb +++ b/lib/redmine/syntax_highlighting.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/themes.rb b/lib/redmine/themes.rb index 3c40bb176..75e1f4c3b 100644 --- a/lib/redmine/themes.rb +++ b/lib/redmine/themes.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/thumbnail.rb b/lib/redmine/thumbnail.rb index 79eaf4ab7..5779551da 100644 --- a/lib/redmine/thumbnail.rb +++ b/lib/redmine/thumbnail.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/unified_diff.rb b/lib/redmine/unified_diff.rb index 67365b070..7e2e20286 100644 --- a/lib/redmine/unified_diff.rb +++ b/lib/redmine/unified_diff.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/utils.rb b/lib/redmine/utils.rb index 85d3a4b33..d255dfa91 100644 --- a/lib/redmine/utils.rb +++ b/lib/redmine/utils.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -64,6 +64,9 @@ module Redmine end module Shell + + module_function + def shell_quote(str) if Redmine::Platform.mswin? '"' + str.gsub(/"/, '\\"') + '"' @@ -71,6 +74,14 @@ module Redmine "'" + str.gsub(/'/, "'\"'\"'") + "'" end end + + def shell_quote_command(command) + if Redmine::Platform.mswin? && RUBY_PLATFORM == 'java' + command + else + shell_quote(command) + end + end end module DateCalculation diff --git a/lib/redmine/version.rb b/lib/redmine/version.rb index 3229bafea..fcfab338c 100644 --- a/lib/redmine/version.rb +++ b/lib/redmine/version.rb @@ -3,8 +3,8 @@ require 'rexml/document' module Redmine module VERSION #:nodoc: MAJOR = 3 - MINOR = 3 - TINY = 9 + MINOR = 4 + TINY = 7 # Branch values: # * official release: nil diff --git a/lib/redmine/views/api_template_handler.rb b/lib/redmine/views/api_template_handler.rb index 98e568093..357badb0d 100644 --- a/lib/redmine/views/api_template_handler.rb +++ b/lib/redmine/views/api_template_handler.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/views/builders.rb b/lib/redmine/views/builders.rb index c36de0cb2..fd98e6d5b 100644 --- a/lib/redmine/views/builders.rb +++ b/lib/redmine/views/builders.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/views/builders/json.rb b/lib/redmine/views/builders/json.rb index 0729b42c2..97ac1250a 100644 --- a/lib/redmine/views/builders/json.rb +++ b/lib/redmine/views/builders/json.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/views/builders/structure.rb b/lib/redmine/views/builders/structure.rb index 420190fc9..7c792792a 100644 --- a/lib/redmine/views/builders/structure.rb +++ b/lib/redmine/views/builders/structure.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/views/builders/xml.rb b/lib/redmine/views/builders/xml.rb index 9d6ca9db0..162de7a5e 100644 --- a/lib/redmine/views/builders/xml.rb +++ b/lib/redmine/views/builders/xml.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/views/labelled_form_builder.rb b/lib/redmine/views/labelled_form_builder.rb index 965dee8c1..7481d79b8 100644 --- a/lib/redmine/views/labelled_form_builder.rb +++ b/lib/redmine/views/labelled_form_builder.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -42,6 +42,15 @@ class Redmine::Views::LabelledFormBuilder < ActionView::Helpers::FormBuilder label_for_field(field, options) + super(field, priority_zones, options, html_options.except(:label)).html_safe end + # A field for entering hours value + def hours_field(field, options={}) + # display the value before type cast when the entered value is not valid + if @object.errors[field].blank? + options = options.merge(:value => format_hours(@object.send field)) + end + text_field field, options + end + # Returns a label tag for the given field def label_for_field(field, options = {}) return ''.html_safe if options.delete(:no_label) diff --git a/lib/redmine/views/other_formats_builder.rb b/lib/redmine/views/other_formats_builder.rb index 48af0fbcb..c022055db 100644 --- a/lib/redmine/views/other_formats_builder.rb +++ b/lib/redmine/views/other_formats_builder.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -28,6 +28,16 @@ module Redmine html_options = { :class => name.to_s.downcase, :rel => 'nofollow' }.merge(options) @view.content_tag('span', @view.link_to(caption, url, html_options)) end + + # Preserves query parameters + def link_to_with_query_parameters(name, url={}, options={}) + params = @view.request.query_parameters.except(:page, :format).except(*url.keys) + url = {:params => params, :page => nil, :format => name.to_s.downcase}.merge(url) + + caption = options.delete(:caption) || name + html_options = { :class => name.to_s.downcase, :rel => 'nofollow' }.merge(options) + @view.content_tag('span', @view.link_to(caption, url, html_options)) + end end end end diff --git a/lib/redmine/wiki_formatting.rb b/lib/redmine/wiki_formatting.rb index 17348d16e..bb9089218 100644 --- a/lib/redmine/wiki_formatting.rb +++ b/lib/redmine/wiki_formatting.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -37,7 +37,7 @@ module Redmine args : %w(Formatter Helper HtmlParser).map {|m| "Redmine::WikiFormatting::#{name.classify}::#{m}".constantize rescue nil} - raise "A formatter class is required" if formatter.nil? + raise "A formatter class is required" if formatter.nil? @@formatters[name] = { :formatter => formatter, @@ -153,7 +153,7 @@ module Redmine # Destructively replaces email addresses into clickable links def auto_mailto!(text) - text.gsub!(/([\w\.!#\$%\-+.\/]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do + text.gsub!(/((?]*>(.*)(#{Regexp.escape(mail)})(.*)<\/a>/) mail @@ -161,7 +161,7 @@ module Redmine %().html_safe end end - end + end end # Default formatter module diff --git a/lib/redmine/wiki_formatting/html_parser.rb b/lib/redmine/wiki_formatting/html_parser.rb index a6d3d8b11..da8ce81b1 100644 --- a/lib/redmine/wiki_formatting/html_parser.rb +++ b/lib/redmine/wiki_formatting/html_parser.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/wiki_formatting/macros.rb b/lib/redmine/wiki_formatting/macros.rb index 1ec6035f7..f4700f242 100644 --- a/lib/redmine/wiki_formatting/macros.rb +++ b/lib/redmine/wiki_formatting/macros.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/wiki_formatting/markdown/formatter.rb b/lib/redmine/wiki_formatting/markdown/formatter.rb index 229692779..c7611d977 100644 --- a/lib/redmine/wiki_formatting/markdown/formatter.rb +++ b/lib/redmine/wiki_formatting/markdown/formatter.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -66,6 +66,10 @@ module Redmine html.gsub!(/(\w):"(.+?)"/) do "#{$1}:\"#{$2}\"" end + # restore user links with @ in login name eg. [@jsmith@somenet.foo] + html.gsub!(%r{[@\A](.*?)}) do + "@#{$2}" + end html end diff --git a/lib/redmine/wiki_formatting/markdown/helper.rb b/lib/redmine/wiki_formatting/markdown/helper.rb index f864988ea..f41fee61c 100644 --- a/lib/redmine/wiki_formatting/markdown/helper.rb +++ b/lib/redmine/wiki_formatting/markdown/helper.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -35,6 +35,7 @@ module Redmine javascript_include_tag('jstoolbar/jstoolbar') + javascript_include_tag('jstoolbar/markdown') + javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language.to_s.downcase}") + + javascript_tag("var wikiImageMimeTypes = #{Redmine::MimeType.by_type('image').to_json};") + stylesheet_link_tag('jstoolbar') end @heads_for_wiki_formatter_included = true diff --git a/lib/redmine/wiki_formatting/markdown/html_parser.rb b/lib/redmine/wiki_formatting/markdown/html_parser.rb index e3d29292d..cee3a2f07 100644 --- a/lib/redmine/wiki_formatting/markdown/html_parser.rb +++ b/lib/redmine/wiki_formatting/markdown/html_parser.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine/wiki_formatting/textile/formatter.rb b/lib/redmine/wiki_formatting/textile/formatter.rb index 4d4e4b240..6e7f28e62 100644 --- a/lib/redmine/wiki_formatting/textile/formatter.rb +++ b/lib/redmine/wiki_formatting/textile/formatter.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -15,7 +15,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -require 'redcloth3' +require File.expand_path('../redcloth3', __FILE__) require 'digest/md5' module Redmine diff --git a/lib/redmine/wiki_formatting/textile/helper.rb b/lib/redmine/wiki_formatting/textile/helper.rb index eedbe6de8..92c7c880f 100644 --- a/lib/redmine/wiki_formatting/textile/helper.rb +++ b/lib/redmine/wiki_formatting/textile/helper.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -35,6 +35,7 @@ module Redmine content_for :header_tags do javascript_include_tag('jstoolbar/jstoolbar-textile.min') + javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language.to_s.downcase}") + + javascript_tag("var wikiImageMimeTypes = #{Redmine::MimeType.by_type('image').to_json};") + stylesheet_link_tag('jstoolbar') end @heads_for_wiki_formatter_included = true diff --git a/lib/redmine/wiki_formatting/textile/html_parser.rb b/lib/redmine/wiki_formatting/textile/html_parser.rb index a5696cb48..3d3cd591e 100644 --- a/lib/redmine/wiki_formatting/textile/html_parser.rb +++ b/lib/redmine/wiki_formatting/textile/html_parser.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redcloth3.rb b/lib/redmine/wiki_formatting/textile/redcloth3.rb similarity index 99% rename from lib/redcloth3.rb rename to lib/redmine/wiki_formatting/textile/redcloth3.rb index d0bd217d3..fc71d5929 100644 --- a/lib/redcloth3.rb +++ b/lib/redmine/wiki_formatting/textile/redcloth3.rb @@ -343,7 +343,7 @@ class RedCloth3 < String A_VLGN = /[\-^~]/ C_CLAS = '(?:\([^")]+\))' C_LNGE = '(?:\[[a-z\-_]+\])' - C_STYL = '(?:\{[^"}]+\})' + C_STYL = '(?:\{[^{][^"}]+\})' S_CSPN = '(?:\\\\\d+)' S_RSPN = '(?:/\d+)' A = "(?:#{A_HLGN}?#{A_VLGN}?|#{A_VLGN}?#{A_HLGN}?)" @@ -969,7 +969,7 @@ class RedCloth3 < String href, alt_title = check_refs( href ) if href url, url_title = check_refs( url ) - return m unless uri_with_safe_scheme?(url) + next m unless uri_with_safe_scheme?(url) out = '' out << "" if href @@ -1034,7 +1034,7 @@ class RedCloth3 < String def flush_left( text ) indt = 0 if text =~ /^ / - while text !~ /^ {#{indt}}\S/ + while text !~ /^ {#{indt}}[^ ]/ indt += 1 end unless text.empty? if indt.nonzero? @@ -1049,7 +1049,7 @@ class RedCloth3 < String end 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_CLOSE = /<\/?#{ OFFTAGS }/ HASTAG_MATCH = /(<\/?\w[^\n]*?>)/m diff --git a/lib/tasks/ciphering.rake b/lib/tasks/ciphering.rake index 7c9ef0782..2b1183352 100644 --- a/lib/tasks/ciphering.rake +++ b/lib/tasks/ciphering.rake @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/tasks/email.rake b/lib/tasks/email.rake index 1b62abb23..305a3e5a2 100644 --- a/lib/tasks/email.rake +++ b/lib/tasks/email.rake @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/tasks/locales.rake b/lib/tasks/locales.rake index 7d2ecfe80..028a25154 100644 --- a/lib/tasks/locales.rake +++ b/lib/tasks/locales.rake @@ -111,7 +111,7 @@ END_DESC files.each do |path| # Skip certain locales (puts "Skipping #{path}"; next) if File.basename(path, ".yml") =~ skips - # TODO: Check for dupliate/existing keys + # TODO: Check for duplicate/existing keys puts "Adding #{key_list} to #{path}" File.open(path, 'a') do |file| adds.each do |kv| diff --git a/lib/tasks/migrate_from_mantis.rake b/lib/tasks/migrate_from_mantis.rake index dba77ef45..61c42325d 100644 --- a/lib/tasks/migrate_from_mantis.rake +++ b/lib/tasks/migrate_from_mantis.rake @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -237,7 +237,7 @@ task :migrate_from_mantis => :environment do # Users print "Migrating users" - User.delete_all "login <> 'admin'" + User.where("login <> 'admin'").delete_all users_map = {} users_migrated = 0 MantisUser.all.each do |user| diff --git a/lib/tasks/migrate_from_trac.rake b/lib/tasks/migrate_from_trac.rake index 955614274..2b8eeea89 100644 --- a/lib/tasks/migrate_from_trac.rake +++ b/lib/tasks/migrate_from_trac.rake @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/tasks/redmine.rake b/lib/tasks/redmine.rake index 973e63e29..fa4b02b33 100644 --- a/lib/tasks/redmine.rake +++ b/lib/tasks/redmine.rake @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -26,6 +26,11 @@ namespace :redmine do task :move_to_subdirectories => :environment do Attachment.move_from_root_to_target_directory end + + desc 'Updates attachment digests to SHA256' + task :update_digests => :environment do + Attachment.update_digests_to_sha256 + end end namespace :tokens do @@ -103,6 +108,9 @@ DESC Target.connection.reset_pk_sequence!(table_name) if Target.primary_key target_count = Target.count abort "Some records were not migrated" unless source_count == target_count + + Object.send(:remove_const, :Target) + Object.send(:remove_const, :Source) end end diff --git a/lib/tasks/reminder.rake b/lib/tasks/reminder.rake index 7e7ef6c11..56122ca7b 100644 --- a/lib/tasks/reminder.rake +++ b/lib/tasks/reminder.rake @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2016 Jean-Philippe Lang +# Copyright (C) 2006-2017 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/log/delete.me b/log/delete.me index 18beddaa8..310d5088e 100644 --- a/log/delete.me +++ b/log/delete.me @@ -1 +1 @@ -default directory for uploaded files \ No newline at end of file +default directory for log files diff --git a/public/help/ar/wiki_syntax_detailed_markdown.html b/public/help/ar/wiki_syntax_detailed_markdown.html index b42394882..cc4eb7025 100644 --- a/public/help/ar/wiki_syntax_detailed_markdown.html +++ b/public/help/ar/wiki_syntax_detailed_markdown.html @@ -3,33 +3,7 @@ RedmineWikiFormatting (Markdown) - + @@ -162,6 +136,16 @@
+
    +
  • Users: +
      +
    • user#2 (link to user with id 2)
    • +
    • user:jsmith (Link to user with login jsmith)
    • +
    • @jsmith (Link to user with login jsmith)
    • +
    +
  • +
+

Escaping:

    @@ -171,7 +155,7 @@

    External links

    -

    HTTP URLs and email addresses are automatically turned into clickable links:

    +

    URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

     http://www.redmine.org, someone@foo.bar
    diff --git a/public/help/ar/wiki_syntax_detailed_textile.html b/public/help/ar/wiki_syntax_detailed_textile.html
    index 09f3210ef..78074b41d 100644
    --- a/public/help/ar/wiki_syntax_detailed_textile.html
    +++ b/public/help/ar/wiki_syntax_detailed_textile.html
    @@ -3,33 +3,7 @@
     
     RedmineWikiFormatting
     
    -
    +
     
     
     
    @@ -153,6 +127,16 @@
                     
+
    +
  • Users: +
      +
    • user#2 (link to user with id 2)
    • +
    • user:jsmith (Link to user with login jsmith)
    • +
    • @jsmith (Link to user with login jsmith)
    • +
    +
  • +
+

Escaping:

    @@ -162,7 +146,7 @@

    External links

    -

    HTTP URLs and email addresses are automatically turned into clickable links:

    +

    URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

     http://www.redmine.org, someone@foo.bar
    diff --git a/public/help/ar/wiki_syntax_markdown.html b/public/help/ar/wiki_syntax_markdown.html
    index c6f6eff5a..39f8a3c26 100644
    --- a/public/help/ar/wiki_syntax_markdown.html
    +++ b/public/help/ar/wiki_syntax_markdown.html
    @@ -2,21 +2,8 @@
     
     
     
    -
     Wiki formatting
    -
    +
     
     
     
    diff --git a/public/help/ar/wiki_syntax_textile.html b/public/help/ar/wiki_syntax_textile.html
    index 55a900dcf..6f544d2a0 100644
    --- a/public/help/ar/wiki_syntax_textile.html
    +++ b/public/help/ar/wiki_syntax_textile.html
    @@ -2,21 +2,8 @@
     
     
     
    -
     Wiki formatting
    -
    +
     
     
     
    diff --git a/public/help/az/wiki_syntax_detailed_markdown.html b/public/help/az/wiki_syntax_detailed_markdown.html
    index b42394882..cc4eb7025 100644
    --- a/public/help/az/wiki_syntax_detailed_markdown.html
    +++ b/public/help/az/wiki_syntax_detailed_markdown.html
    @@ -3,33 +3,7 @@
     
     RedmineWikiFormatting (Markdown)
     
    -
    +
     
     
     
    @@ -162,6 +136,16 @@
                 
             
+
    +
  • Users: +
      +
    • user#2 (link to user with id 2)
    • +
    • user:jsmith (Link to user with login jsmith)
    • +
    • @jsmith (Link to user with login jsmith)
    • +
    +
  • +
+

Escaping:

    @@ -171,7 +155,7 @@

    External links

    -

    HTTP URLs and email addresses are automatically turned into clickable links:

    +

    URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

     http://www.redmine.org, someone@foo.bar
    diff --git a/public/help/az/wiki_syntax_detailed_textile.html b/public/help/az/wiki_syntax_detailed_textile.html
    index 09f3210ef..78074b41d 100644
    --- a/public/help/az/wiki_syntax_detailed_textile.html
    +++ b/public/help/az/wiki_syntax_detailed_textile.html
    @@ -3,33 +3,7 @@
     
     RedmineWikiFormatting
     
    -
    +
     
     
     
    @@ -153,6 +127,16 @@
                     
+
    +
  • Users: +
      +
    • user#2 (link to user with id 2)
    • +
    • user:jsmith (Link to user with login jsmith)
    • +
    • @jsmith (Link to user with login jsmith)
    • +
    +
  • +
+

Escaping:

    @@ -162,7 +146,7 @@

    External links

    -

    HTTP URLs and email addresses are automatically turned into clickable links:

    +

    URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

     http://www.redmine.org, someone@foo.bar
    diff --git a/public/help/az/wiki_syntax_markdown.html b/public/help/az/wiki_syntax_markdown.html
    index c6f6eff5a..39f8a3c26 100644
    --- a/public/help/az/wiki_syntax_markdown.html
    +++ b/public/help/az/wiki_syntax_markdown.html
    @@ -2,21 +2,8 @@
     
     
     
    -
     Wiki formatting
    -
    +
     
     
     
    diff --git a/public/help/az/wiki_syntax_textile.html b/public/help/az/wiki_syntax_textile.html
    index 55a900dcf..6f544d2a0 100644
    --- a/public/help/az/wiki_syntax_textile.html
    +++ b/public/help/az/wiki_syntax_textile.html
    @@ -2,21 +2,8 @@
     
     
     
    -
     Wiki formatting
    -
    +
     
     
     
    diff --git a/public/help/bg/wiki_syntax_detailed_markdown.html b/public/help/bg/wiki_syntax_detailed_markdown.html
    index b42394882..cc4eb7025 100644
    --- a/public/help/bg/wiki_syntax_detailed_markdown.html
    +++ b/public/help/bg/wiki_syntax_detailed_markdown.html
    @@ -3,33 +3,7 @@
     
     RedmineWikiFormatting (Markdown)
     
    -
    +
     
     
     
    @@ -162,6 +136,16 @@
                 
             
+
    +
  • Users: +
      +
    • user#2 (link to user with id 2)
    • +
    • user:jsmith (Link to user with login jsmith)
    • +
    • @jsmith (Link to user with login jsmith)
    • +
    +
  • +
+

Escaping:

    @@ -171,7 +155,7 @@

    External links

    -

    HTTP URLs and email addresses are automatically turned into clickable links:

    +

    URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

     http://www.redmine.org, someone@foo.bar
    diff --git a/public/help/bg/wiki_syntax_detailed_textile.html b/public/help/bg/wiki_syntax_detailed_textile.html
    index 09f3210ef..78074b41d 100644
    --- a/public/help/bg/wiki_syntax_detailed_textile.html
    +++ b/public/help/bg/wiki_syntax_detailed_textile.html
    @@ -3,33 +3,7 @@
     
     RedmineWikiFormatting
     
    -
    +
     
     
     
    @@ -153,6 +127,16 @@
                     
+
    +
  • Users: +
      +
    • user#2 (link to user with id 2)
    • +
    • user:jsmith (Link to user with login jsmith)
    • +
    • @jsmith (Link to user with login jsmith)
    • +
    +
  • +
+

Escaping:

    @@ -162,7 +146,7 @@

    External links

    -

    HTTP URLs and email addresses are automatically turned into clickable links:

    +

    URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

     http://www.redmine.org, someone@foo.bar
    diff --git a/public/help/bg/wiki_syntax_markdown.html b/public/help/bg/wiki_syntax_markdown.html
    index c6f6eff5a..39f8a3c26 100644
    --- a/public/help/bg/wiki_syntax_markdown.html
    +++ b/public/help/bg/wiki_syntax_markdown.html
    @@ -2,21 +2,8 @@
     
     
     
    -
     Wiki formatting
    -
    +
     
     
     
    diff --git a/public/help/bg/wiki_syntax_textile.html b/public/help/bg/wiki_syntax_textile.html
    index 55a900dcf..6f544d2a0 100644
    --- a/public/help/bg/wiki_syntax_textile.html
    +++ b/public/help/bg/wiki_syntax_textile.html
    @@ -2,21 +2,8 @@
     
     
     
    -
     Wiki formatting
    -
    +
     
     
     
    diff --git a/public/help/bs/wiki_syntax_detailed_markdown.html b/public/help/bs/wiki_syntax_detailed_markdown.html
    index b42394882..cc4eb7025 100644
    --- a/public/help/bs/wiki_syntax_detailed_markdown.html
    +++ b/public/help/bs/wiki_syntax_detailed_markdown.html
    @@ -3,33 +3,7 @@
     
     RedmineWikiFormatting (Markdown)
     
    -
    +
     
     
     
    @@ -162,6 +136,16 @@
                 
             
+
    +
  • Users: +
      +
    • user#2 (link to user with id 2)
    • +
    • user:jsmith (Link to user with login jsmith)
    • +
    • @jsmith (Link to user with login jsmith)
    • +
    +
  • +
+

Escaping:

    @@ -171,7 +155,7 @@

    External links

    -

    HTTP URLs and email addresses are automatically turned into clickable links:

    +

    URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

     http://www.redmine.org, someone@foo.bar
    diff --git a/public/help/bs/wiki_syntax_detailed_textile.html b/public/help/bs/wiki_syntax_detailed_textile.html
    index 09f3210ef..78074b41d 100644
    --- a/public/help/bs/wiki_syntax_detailed_textile.html
    +++ b/public/help/bs/wiki_syntax_detailed_textile.html
    @@ -3,33 +3,7 @@
     
     RedmineWikiFormatting
     
    -
    +
     
     
     
    @@ -153,6 +127,16 @@
                     
+
    +
  • Users: +
      +
    • user#2 (link to user with id 2)
    • +
    • user:jsmith (Link to user with login jsmith)
    • +
    • @jsmith (Link to user with login jsmith)
    • +
    +
  • +
+

Escaping:

    @@ -162,7 +146,7 @@

    External links

    -

    HTTP URLs and email addresses are automatically turned into clickable links:

    +

    URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

     http://www.redmine.org, someone@foo.bar
    diff --git a/public/help/bs/wiki_syntax_markdown.html b/public/help/bs/wiki_syntax_markdown.html
    index c6f6eff5a..39f8a3c26 100644
    --- a/public/help/bs/wiki_syntax_markdown.html
    +++ b/public/help/bs/wiki_syntax_markdown.html
    @@ -2,21 +2,8 @@
     
     
     
    -
     Wiki formatting
    -
    +
     
     
     
    diff --git a/public/help/bs/wiki_syntax_textile.html b/public/help/bs/wiki_syntax_textile.html
    index 55a900dcf..6f544d2a0 100644
    --- a/public/help/bs/wiki_syntax_textile.html
    +++ b/public/help/bs/wiki_syntax_textile.html
    @@ -2,21 +2,8 @@
     
     
     
    -
     Wiki formatting
    -
    +
     
     
     
    diff --git a/public/help/ca/wiki_syntax_detailed_markdown.html b/public/help/ca/wiki_syntax_detailed_markdown.html
    index b42394882..cc4eb7025 100644
    --- a/public/help/ca/wiki_syntax_detailed_markdown.html
    +++ b/public/help/ca/wiki_syntax_detailed_markdown.html
    @@ -3,33 +3,7 @@
     
     RedmineWikiFormatting (Markdown)
     
    -
    +
     
     
     
    @@ -162,6 +136,16 @@
                 
             
+
    +
  • Users: +
      +
    • user#2 (link to user with id 2)
    • +
    • user:jsmith (Link to user with login jsmith)
    • +
    • @jsmith (Link to user with login jsmith)
    • +
    +
  • +
+

Escaping:

    @@ -171,7 +155,7 @@

    External links

    -

    HTTP URLs and email addresses are automatically turned into clickable links:

    +

    URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

     http://www.redmine.org, someone@foo.bar
    diff --git a/public/help/ca/wiki_syntax_detailed_textile.html b/public/help/ca/wiki_syntax_detailed_textile.html
    index 09f3210ef..78074b41d 100644
    --- a/public/help/ca/wiki_syntax_detailed_textile.html
    +++ b/public/help/ca/wiki_syntax_detailed_textile.html
    @@ -3,33 +3,7 @@
     
     RedmineWikiFormatting
     
    -
    +
     
     
     
    @@ -153,6 +127,16 @@
                     
+
    +
  • Users: +
      +
    • user#2 (link to user with id 2)
    • +
    • user:jsmith (Link to user with login jsmith)
    • +
    • @jsmith (Link to user with login jsmith)
    • +
    +
  • +
+

Escaping:

    @@ -162,7 +146,7 @@

    External links

    -

    HTTP URLs and email addresses are automatically turned into clickable links:

    +

    URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

     http://www.redmine.org, someone@foo.bar
    diff --git a/public/help/ca/wiki_syntax_markdown.html b/public/help/ca/wiki_syntax_markdown.html
    index 237a0c12e..3cad889a2 100644
    --- a/public/help/ca/wiki_syntax_markdown.html
    +++ b/public/help/ca/wiki_syntax_markdown.html
    @@ -2,21 +2,8 @@
     
     
     
    -
     Format de la Wiki
    -
    +
     
     
     
    diff --git a/public/help/ca/wiki_syntax_textile.html b/public/help/ca/wiki_syntax_textile.html
    index 5d81a44b1..b76739a38 100644
    --- a/public/help/ca/wiki_syntax_textile.html
    +++ b/public/help/ca/wiki_syntax_textile.html
    @@ -2,21 +2,8 @@
     
     
     
    -
     Format de la Wiki
    -
    +
     
     
     
    diff --git a/public/help/cs/wiki_syntax_detailed_markdown.html b/public/help/cs/wiki_syntax_detailed_markdown.html
    index 3762debf9..ea554565f 100644
    --- a/public/help/cs/wiki_syntax_detailed_markdown.html
    +++ b/public/help/cs/wiki_syntax_detailed_markdown.html
    @@ -3,33 +3,7 @@
     
     Formátování Wiki v Redminu
     
    -
    +
     
     
     
    @@ -71,7 +45,8 @@
                         
  • document:Úvod (odkaz na dokument s názvem "Úvod")
  • document:"Nějaký dokument" (Uvozovky se mohou použít v případě, že název obsahuje mezery.)
  • projekt_test:document:"Nějaký dokument" (odkaz na dokument s názvem "Nějaký dokument" v jiném projektu "projekt_test")
  • -
+ +
    @@ -81,7 +56,8 @@
  • version:1.0.0 odkaz na verzi s názvem "1.0.0")
  • version:"1.0 beta 2"
  • projekt_test:version:1.0.0 (odkaz na verzi "1.0.0" jiného projektu "projekt_test")
  • -
+ +
    @@ -89,7 +65,8 @@
    • attachment:soubor.zip (odkaz na přílohu aktuálního objektu s názvem soubor.zip)
    • Aktuálně mohou být odkazovány pouze přílohy aktuálního objektu (u úkolu mohou být odkazy pouze na přílohy danného úkolu).
    • -
    +
+
    @@ -101,7 +78,8 @@
  • commit:hg|c6f4d0fd (odkaz na revizi s nečíselným označním revize určitého repozitáře, pro projekty s více repozitáři)
  • projekt_test:r758 (odkaz na revizi jiného projektu)
  • projekt_test:commit:c6f4d0fd (odkaz na revizi s nečíselným označním revize jiného projektu)
  • -
+ +
    @@ -116,7 +94,8 @@
  • source:svn1|some/file (odkaz na soubor určitého repozitáře, pro projekty s více repositáři)
  • projekt_test:source:some/file (odkaz na soubor umístěný v /some/file repositáře projektu "projekt_test")
  • projekt_test:export:some/file (vynutit stažení souboru umístěného v /some/file repositáře projektu "projekt_test")
  • -
+ +
    @@ -125,24 +104,26 @@
  • forum#1 (odkaz na fórum s id 1
  • forum:Support (odkaz na fórum pojmenované Support)
  • forum:"Technical Support" (Použij dvojté uvozovkym jestliže název fóra obsahuje mezery.)
  • -
+ +
  • Příspěvky diskuzního fóra:
    • message#1218 (odkaz na příspěvek s ID 1218)
    • -
  • +
+ -
  • Projekty:
    • project#3 (odkaz na projekt s ID 3)
    • project:projekt_test (odkaz na projekt pojmenovaný "projekt_test")
    • project:"projekt test" (odkaz na projekt pojmenovaný "projekt test")
    • -
  • +
+
    @@ -151,7 +132,18 @@
  • news#2 (odkaz na novinku id 2)
  • news:Greetings (odkaz na novinku "Greetings")
  • news:"First Release" (použij dvojté uvozovky, jestliže název novinky obsahuje mezery)
  • -
+ + + + +
    +
  • Uživatelé: +
      +
    • user#2 (odkaz na uživatele s id 2)
    • +
    • user:jsmith (odkaz na uživatele s loginem jsmith)
    • +
    • @jsmith (odkaz na uživatele s loginem jsmith)
    • +
    +

Escape sekvence:

@@ -160,10 +152,9 @@
  • Zabránit parsování Redmine odkazů lze vložením vykřičníku před odkaz: !
  • -

    Externí odkazy

    -

    URL odkazy a emaily jsou automaticky zobrazeny jako klikací odkaz:

    +

    URL (začínající: www, http, https, ftp, ftps, sftp a sftps) a e-mailové adresy jsou automaticky převedeny na klikací odkazy:

     http://www.redmine.org, someone@foo.bar
    diff --git a/public/help/cs/wiki_syntax_detailed_textile.html b/public/help/cs/wiki_syntax_detailed_textile.html
    index e45c2f45c..f34379b34 100644
    --- a/public/help/cs/wiki_syntax_detailed_textile.html
    +++ b/public/help/cs/wiki_syntax_detailed_textile.html
    @@ -3,33 +3,7 @@
     
     Formátování Wiki v Redminu
     
    -
    +
     
     
     
    @@ -71,7 +45,8 @@
                         
  • document:Úvod (odkaz na dokument s názvem "Úvod")
  • document:"Nějaký dokument" (Uvozovky se mohou použít v případě, že název obsahuje mezery.)
  • projekt_test:document:"Nějaký dokument" (odkaz na dokument s názvem "Nějaký dokument" v jiném projektu "projekt_test")
  • - + +
      @@ -81,7 +56,8 @@
    • version:1.0.0 odkaz na verzi s názvem "1.0.0")
    • version:"1.0 beta 2"
    • projekt_test:version:1.0.0 (odkaz na verzi "1.0.0" jiného projektu "projekt_test")
    • -
    + +
      @@ -89,7 +65,8 @@
      • attachment:soubor.zip (odkaz na přílohu aktuálního objektu s názvem soubor.zip)
      • Aktuálně mohou být odkazovány pouze přílohy aktuálního objektu (u úkolu mohou být odkazy pouze na přílohy danného úkolu).
      • -
      +
    +
      @@ -101,7 +78,8 @@
    • commit:hg|c6f4d0fd (odkaz na revizi s nečíselným označním revize určitého repozitáře, pro projekty s více repozitáři)
    • projekt_test:r758 (odkaz na revizi jiného projektu)
    • projekt_test:commit:c6f4d0fd (odkaz na revizi s nečíselným označním revize jiného projektu)
    • -
    + +
      @@ -116,7 +94,8 @@
    • source:svn1|some/file (odkaz na soubor určitého repozitáře, pro projekty s více repositáři)
    • projekt_test:source:some/file (odkaz na soubor umístěný v /some/file repositáře projektu "projekt_test")
    • projekt_test:export:some/file (vynutit stažení souboru umístěného v /some/file repositáře projektu "projekt_test")
    • -
    + +
      @@ -125,14 +104,16 @@
    • forum#1 (odkaz na fórum s id 1
    • forum:Support (odkaz na fórum pojmenované Support)
    • forum:"Technical Support" (Použij dvojté uvozovkym jestliže název fóra obsahuje mezery.)
    • -
    + +
    • Příspěvky diskuzního fóra:
      • message#1218 (odkaz na příspěvek s ID 1218)
      • -
    • +
    +
      @@ -141,7 +122,8 @@
    • project#3 (odkaz na projekt s ID 3)
    • project:projekt_test (odkaz na projekt pojmenovaný "projekt_test")
    • project:"projekt test" (odkaz na projekt pojmenovaný "projekt test")
    • -
    + +
      @@ -150,7 +132,18 @@
    • news#2 (odkaz na novinku id 2)
    • news:Greetings (odkaz na novinku "Greetings")
    • news:"First Release" (použij dvojté uvozovky, jestliže název novinky obsahuje mezery)
    • -
    + + + + +
      +
    • Uživatelé: +
        +
      • user#2 (odkaz na uživatele s id 2)
      • +
      • user:jsmith (odkaz na uživatele s loginem jsmith)
      • +
      • @jsmith (odkaz na uživatele s loginem jsmith)
      • +
      +

    Escape sekvence:

    @@ -162,7 +155,7 @@

    Externí odkazy

    -

    URL odkazy a emaily jsou automaticky zobrazeny jako klikací odkaz:

    +

    URL (začínající: www, http, https, ftp, ftps, sftp a sftps) a e-mailové adresy jsou automaticky převedeny na klikací odkazy:

     http://www.redmine.org, someone@foo.bar
    @@ -232,7 +225,6 @@ p=. zarovnaný na střed
     
             

    Toto je odstavec zarovnaný na střed.

    -

    Citace

    Začněte odstavec s bq.

    diff --git a/public/help/cs/wiki_syntax_markdown.html b/public/help/cs/wiki_syntax_markdown.html index b437dc941..57131af0d 100644 --- a/public/help/cs/wiki_syntax_markdown.html +++ b/public/help/cs/wiki_syntax_markdown.html @@ -2,21 +2,8 @@ - Wiki formátování - + diff --git a/public/help/cs/wiki_syntax_textile.html b/public/help/cs/wiki_syntax_textile.html index bdb8180cb..12cf640f9 100644 --- a/public/help/cs/wiki_syntax_textile.html +++ b/public/help/cs/wiki_syntax_textile.html @@ -2,21 +2,8 @@ - Wiki formátování - + diff --git a/public/help/da/wiki_syntax_detailed_markdown.html b/public/help/da/wiki_syntax_detailed_markdown.html index b42394882..cc4eb7025 100644 --- a/public/help/da/wiki_syntax_detailed_markdown.html +++ b/public/help/da/wiki_syntax_detailed_markdown.html @@ -3,33 +3,7 @@ RedmineWikiFormatting (Markdown) - + @@ -162,6 +136,16 @@ +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/da/wiki_syntax_detailed_textile.html b/public/help/da/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/da/wiki_syntax_detailed_textile.html
      +++ b/public/help/da/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/da/wiki_syntax_markdown.html b/public/help/da/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/da/wiki_syntax_markdown.html
      +++ b/public/help/da/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/da/wiki_syntax_textile.html b/public/help/da/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/da/wiki_syntax_textile.html
      +++ b/public/help/da/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/de/wiki_syntax_detailed_markdown.html b/public/help/de/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/de/wiki_syntax_detailed_markdown.html
      +++ b/public/help/de/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/de/wiki_syntax_detailed_textile.html b/public/help/de/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/de/wiki_syntax_detailed_textile.html
      +++ b/public/help/de/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/de/wiki_syntax_markdown.html b/public/help/de/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/de/wiki_syntax_markdown.html
      +++ b/public/help/de/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/de/wiki_syntax_textile.html b/public/help/de/wiki_syntax_textile.html
      index e25964337..a3a336516 100644
      --- a/public/help/de/wiki_syntax_textile.html
      +++ b/public/help/de/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wikiformatierung
      -
      +
       
       
       
      diff --git a/public/help/el/wiki_syntax_detailed_markdown.html b/public/help/el/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/el/wiki_syntax_detailed_markdown.html
      +++ b/public/help/el/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/el/wiki_syntax_detailed_textile.html b/public/help/el/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/el/wiki_syntax_detailed_textile.html
      +++ b/public/help/el/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/el/wiki_syntax_markdown.html b/public/help/el/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/el/wiki_syntax_markdown.html
      +++ b/public/help/el/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/el/wiki_syntax_textile.html b/public/help/el/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/el/wiki_syntax_textile.html
      +++ b/public/help/el/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/en-gb/wiki_syntax_detailed_markdown.html b/public/help/en-gb/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/en-gb/wiki_syntax_detailed_markdown.html
      +++ b/public/help/en-gb/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/en-gb/wiki_syntax_detailed_textile.html b/public/help/en-gb/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/en-gb/wiki_syntax_detailed_textile.html
      +++ b/public/help/en-gb/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/en-gb/wiki_syntax_markdown.html b/public/help/en-gb/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/en-gb/wiki_syntax_markdown.html
      +++ b/public/help/en-gb/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/en-gb/wiki_syntax_textile.html b/public/help/en-gb/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/en-gb/wiki_syntax_textile.html
      +++ b/public/help/en-gb/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/en/wiki_syntax_detailed_markdown.html b/public/help/en/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/en/wiki_syntax_detailed_markdown.html
      +++ b/public/help/en/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/en/wiki_syntax_detailed_textile.html b/public/help/en/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/en/wiki_syntax_detailed_textile.html
      +++ b/public/help/en/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/en/wiki_syntax_markdown.html b/public/help/en/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/en/wiki_syntax_markdown.html
      +++ b/public/help/en/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/en/wiki_syntax_textile.html b/public/help/en/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/en/wiki_syntax_textile.html
      +++ b/public/help/en/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/es-pa/wiki_syntax_detailed_markdown.html b/public/help/es-pa/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/es-pa/wiki_syntax_detailed_markdown.html
      +++ b/public/help/es-pa/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/es-pa/wiki_syntax_detailed_textile.html b/public/help/es-pa/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/es-pa/wiki_syntax_detailed_textile.html
      +++ b/public/help/es-pa/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/es-pa/wiki_syntax_markdown.html b/public/help/es-pa/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/es-pa/wiki_syntax_markdown.html
      +++ b/public/help/es-pa/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/es-pa/wiki_syntax_textile.html b/public/help/es-pa/wiki_syntax_textile.html
      index 7fefd5ff2..d859c8eff 100644
      --- a/public/help/es-pa/wiki_syntax_textile.html
      +++ b/public/help/es-pa/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Formato de la Wiki
      -
      +
       
       
       
      diff --git a/public/help/es/wiki_syntax_detailed_markdown.html b/public/help/es/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/es/wiki_syntax_detailed_markdown.html
      +++ b/public/help/es/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/es/wiki_syntax_detailed_textile.html b/public/help/es/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/es/wiki_syntax_detailed_textile.html
      +++ b/public/help/es/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/es/wiki_syntax_markdown.html b/public/help/es/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/es/wiki_syntax_markdown.html
      +++ b/public/help/es/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/es/wiki_syntax_textile.html b/public/help/es/wiki_syntax_textile.html
      index 7fefd5ff2..d859c8eff 100644
      --- a/public/help/es/wiki_syntax_textile.html
      +++ b/public/help/es/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Formato de la Wiki
      -
      +
       
       
       
      diff --git a/public/help/et/wiki_syntax_detailed_markdown.html b/public/help/et/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/et/wiki_syntax_detailed_markdown.html
      +++ b/public/help/et/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/et/wiki_syntax_detailed_textile.html b/public/help/et/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/et/wiki_syntax_detailed_textile.html
      +++ b/public/help/et/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/et/wiki_syntax_markdown.html b/public/help/et/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/et/wiki_syntax_markdown.html
      +++ b/public/help/et/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/et/wiki_syntax_textile.html b/public/help/et/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/et/wiki_syntax_textile.html
      +++ b/public/help/et/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/eu/wiki_syntax_detailed_markdown.html b/public/help/eu/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/eu/wiki_syntax_detailed_markdown.html
      +++ b/public/help/eu/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/eu/wiki_syntax_detailed_textile.html b/public/help/eu/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/eu/wiki_syntax_detailed_textile.html
      +++ b/public/help/eu/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/eu/wiki_syntax_markdown.html b/public/help/eu/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/eu/wiki_syntax_markdown.html
      +++ b/public/help/eu/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/eu/wiki_syntax_textile.html b/public/help/eu/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/eu/wiki_syntax_textile.html
      +++ b/public/help/eu/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/fa/wiki_syntax_detailed_markdown.html b/public/help/fa/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/fa/wiki_syntax_detailed_markdown.html
      +++ b/public/help/fa/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/fa/wiki_syntax_detailed_textile.html b/public/help/fa/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/fa/wiki_syntax_detailed_textile.html
      +++ b/public/help/fa/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/fa/wiki_syntax_markdown.html b/public/help/fa/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/fa/wiki_syntax_markdown.html
      +++ b/public/help/fa/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/fa/wiki_syntax_textile.html b/public/help/fa/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/fa/wiki_syntax_textile.html
      +++ b/public/help/fa/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/fi/wiki_syntax_detailed_markdown.html b/public/help/fi/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/fi/wiki_syntax_detailed_markdown.html
      +++ b/public/help/fi/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/fi/wiki_syntax_detailed_textile.html b/public/help/fi/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/fi/wiki_syntax_detailed_textile.html
      +++ b/public/help/fi/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/fi/wiki_syntax_markdown.html b/public/help/fi/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/fi/wiki_syntax_markdown.html
      +++ b/public/help/fi/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/fi/wiki_syntax_textile.html b/public/help/fi/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/fi/wiki_syntax_textile.html
      +++ b/public/help/fi/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/fr/wiki_syntax_detailed_markdown.html b/public/help/fr/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/fr/wiki_syntax_detailed_markdown.html
      +++ b/public/help/fr/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/fr/wiki_syntax_detailed_textile.html b/public/help/fr/wiki_syntax_detailed_textile.html
      index 6ee0754d0..1ee0d0b8e 100644
      --- a/public/help/fr/wiki_syntax_detailed_textile.html
      +++ b/public/help/fr/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Eviter ces lien:

      @@ -162,7 +146,7 @@

      Liens externes

      -

      Les URLs HTTP et les adresses email sont automatiquement transformé en liens:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/fr/wiki_syntax_markdown.html b/public/help/fr/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/fr/wiki_syntax_markdown.html
      +++ b/public/help/fr/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/fr/wiki_syntax_textile.html b/public/help/fr/wiki_syntax_textile.html
      index 2de9bef14..d74b91ac4 100644
      --- a/public/help/fr/wiki_syntax_textile.html
      +++ b/public/help/fr/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/gl/wiki_syntax_detailed_markdown.html b/public/help/gl/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/gl/wiki_syntax_detailed_markdown.html
      +++ b/public/help/gl/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/gl/wiki_syntax_detailed_textile.html b/public/help/gl/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/gl/wiki_syntax_detailed_textile.html
      +++ b/public/help/gl/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/gl/wiki_syntax_markdown.html b/public/help/gl/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/gl/wiki_syntax_markdown.html
      +++ b/public/help/gl/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/gl/wiki_syntax_textile.html b/public/help/gl/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/gl/wiki_syntax_textile.html
      +++ b/public/help/gl/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/he/wiki_syntax_detailed_markdown.html b/public/help/he/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/he/wiki_syntax_detailed_markdown.html
      +++ b/public/help/he/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/he/wiki_syntax_detailed_textile.html b/public/help/he/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/he/wiki_syntax_detailed_textile.html
      +++ b/public/help/he/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/he/wiki_syntax_markdown.html b/public/help/he/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/he/wiki_syntax_markdown.html
      +++ b/public/help/he/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/he/wiki_syntax_textile.html b/public/help/he/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/he/wiki_syntax_textile.html
      +++ b/public/help/he/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/hr/wiki_syntax_detailed_markdown.html b/public/help/hr/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/hr/wiki_syntax_detailed_markdown.html
      +++ b/public/help/hr/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/hr/wiki_syntax_detailed_textile.html b/public/help/hr/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/hr/wiki_syntax_detailed_textile.html
      +++ b/public/help/hr/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/hr/wiki_syntax_markdown.html b/public/help/hr/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/hr/wiki_syntax_markdown.html
      +++ b/public/help/hr/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/hr/wiki_syntax_textile.html b/public/help/hr/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/hr/wiki_syntax_textile.html
      +++ b/public/help/hr/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/hu/wiki_syntax_detailed_markdown.html b/public/help/hu/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/hu/wiki_syntax_detailed_markdown.html
      +++ b/public/help/hu/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/hu/wiki_syntax_detailed_textile.html b/public/help/hu/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/hu/wiki_syntax_detailed_textile.html
      +++ b/public/help/hu/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/hu/wiki_syntax_markdown.html b/public/help/hu/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/hu/wiki_syntax_markdown.html
      +++ b/public/help/hu/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/hu/wiki_syntax_textile.html b/public/help/hu/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/hu/wiki_syntax_textile.html
      +++ b/public/help/hu/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/id/wiki_syntax_detailed_markdown.html b/public/help/id/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/id/wiki_syntax_detailed_markdown.html
      +++ b/public/help/id/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/id/wiki_syntax_detailed_textile.html b/public/help/id/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/id/wiki_syntax_detailed_textile.html
      +++ b/public/help/id/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/id/wiki_syntax_markdown.html b/public/help/id/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/id/wiki_syntax_markdown.html
      +++ b/public/help/id/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/id/wiki_syntax_textile.html b/public/help/id/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/id/wiki_syntax_textile.html
      +++ b/public/help/id/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/it/wiki_syntax_detailed_markdown.html b/public/help/it/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/it/wiki_syntax_detailed_markdown.html
      +++ b/public/help/it/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/it/wiki_syntax_detailed_textile.html b/public/help/it/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/it/wiki_syntax_detailed_textile.html
      +++ b/public/help/it/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/it/wiki_syntax_markdown.html b/public/help/it/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/it/wiki_syntax_markdown.html
      +++ b/public/help/it/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/it/wiki_syntax_textile.html b/public/help/it/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/it/wiki_syntax_textile.html
      +++ b/public/help/it/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/ja/wiki_syntax_detailed_markdown.html b/public/help/ja/wiki_syntax_detailed_markdown.html
      index fc36822ce..1078ab373 100644
      --- a/public/help/ja/wiki_syntax_detailed_markdown.html
      +++ b/public/help/ja/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • ユーザー: +
        +
      • user#2 (id 2のユーザーへのリンク)
      • +
      • user:jsmith (jsmith というログインIDのユーザーへのリンク)
      • +
      • @jsmith (jsmith というログインIDのユーザーへのリンク)
      • +
      +
    • +
    +

    エスケープ:

      @@ -171,7 +155,7 @@

      外部リンク

      -

      HTTP URLとメールアドレスは自動的にリンクになります:

      +

      URL(starting with: www, http, https, ftp, ftps, sftp and sftps)とメールアドレスは自動的にリンクになります:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/ja/wiki_syntax_detailed_textile.html b/public/help/ja/wiki_syntax_detailed_textile.html
      index 756fab82e..cb5f07da8 100644
      --- a/public/help/ja/wiki_syntax_detailed_textile.html
      +++ b/public/help/ja/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • ユーザー: +
        +
      • user#2 (id 2のユーザーへのリンク)
      • +
      • user:jsmith (jsmith というログインIDのユーザーへのリンク)
      • +
      • @jsmith (jsmith というログインIDのユーザーへのリンク)
      • +
      +
    • +
    +

    エスケープ:

      @@ -162,7 +146,7 @@

      外部リンク

      -

      HTTP URLとメールアドレスは自動的にリンクになります:

      +

      URL(starting with: www, http, https, ftp, ftps, sftp and sftps)とメールアドレスは自動的にリンクになります:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/ja/wiki_syntax_markdown.html b/public/help/ja/wiki_syntax_markdown.html
      index 4da947dcc..e5f6b86ee 100644
      --- a/public/help/ja/wiki_syntax_markdown.html
      +++ b/public/help/ja/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/ja/wiki_syntax_textile.html b/public/help/ja/wiki_syntax_textile.html
      index 1ea52cffc..acb9d8598 100644
      --- a/public/help/ja/wiki_syntax_textile.html
      +++ b/public/help/ja/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/ko/wiki_syntax_detailed_markdown.html b/public/help/ko/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/ko/wiki_syntax_detailed_markdown.html
      +++ b/public/help/ko/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/ko/wiki_syntax_detailed_textile.html b/public/help/ko/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/ko/wiki_syntax_detailed_textile.html
      +++ b/public/help/ko/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/ko/wiki_syntax_markdown.html b/public/help/ko/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/ko/wiki_syntax_markdown.html
      +++ b/public/help/ko/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/ko/wiki_syntax_textile.html b/public/help/ko/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/ko/wiki_syntax_textile.html
      +++ b/public/help/ko/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/lt/wiki_syntax_detailed_markdown.html b/public/help/lt/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/lt/wiki_syntax_detailed_markdown.html
      +++ b/public/help/lt/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/lt/wiki_syntax_detailed_textile.html b/public/help/lt/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/lt/wiki_syntax_detailed_textile.html
      +++ b/public/help/lt/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/lt/wiki_syntax_markdown.html b/public/help/lt/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/lt/wiki_syntax_markdown.html
      +++ b/public/help/lt/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/lt/wiki_syntax_textile.html b/public/help/lt/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/lt/wiki_syntax_textile.html
      +++ b/public/help/lt/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/lv/wiki_syntax_detailed_markdown.html b/public/help/lv/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/lv/wiki_syntax_detailed_markdown.html
      +++ b/public/help/lv/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/lv/wiki_syntax_detailed_textile.html b/public/help/lv/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/lv/wiki_syntax_detailed_textile.html
      +++ b/public/help/lv/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/lv/wiki_syntax_markdown.html b/public/help/lv/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/lv/wiki_syntax_markdown.html
      +++ b/public/help/lv/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/lv/wiki_syntax_textile.html b/public/help/lv/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/lv/wiki_syntax_textile.html
      +++ b/public/help/lv/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/mk/wiki_syntax_detailed_markdown.html b/public/help/mk/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/mk/wiki_syntax_detailed_markdown.html
      +++ b/public/help/mk/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/mk/wiki_syntax_detailed_textile.html b/public/help/mk/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/mk/wiki_syntax_detailed_textile.html
      +++ b/public/help/mk/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/mk/wiki_syntax_markdown.html b/public/help/mk/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/mk/wiki_syntax_markdown.html
      +++ b/public/help/mk/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/mk/wiki_syntax_textile.html b/public/help/mk/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/mk/wiki_syntax_textile.html
      +++ b/public/help/mk/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/mn/wiki_syntax_detailed_markdown.html b/public/help/mn/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/mn/wiki_syntax_detailed_markdown.html
      +++ b/public/help/mn/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/mn/wiki_syntax_detailed_textile.html b/public/help/mn/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/mn/wiki_syntax_detailed_textile.html
      +++ b/public/help/mn/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/mn/wiki_syntax_markdown.html b/public/help/mn/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/mn/wiki_syntax_markdown.html
      +++ b/public/help/mn/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/mn/wiki_syntax_textile.html b/public/help/mn/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/mn/wiki_syntax_textile.html
      +++ b/public/help/mn/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/nl/wiki_syntax_detailed_markdown.html b/public/help/nl/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/nl/wiki_syntax_detailed_markdown.html
      +++ b/public/help/nl/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/nl/wiki_syntax_detailed_textile.html b/public/help/nl/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/nl/wiki_syntax_detailed_textile.html
      +++ b/public/help/nl/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/nl/wiki_syntax_markdown.html b/public/help/nl/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/nl/wiki_syntax_markdown.html
      +++ b/public/help/nl/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/nl/wiki_syntax_textile.html b/public/help/nl/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/nl/wiki_syntax_textile.html
      +++ b/public/help/nl/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/no/wiki_syntax_detailed_markdown.html b/public/help/no/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/no/wiki_syntax_detailed_markdown.html
      +++ b/public/help/no/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/no/wiki_syntax_detailed_textile.html b/public/help/no/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/no/wiki_syntax_detailed_textile.html
      +++ b/public/help/no/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/no/wiki_syntax_markdown.html b/public/help/no/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/no/wiki_syntax_markdown.html
      +++ b/public/help/no/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/no/wiki_syntax_textile.html b/public/help/no/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/no/wiki_syntax_textile.html
      +++ b/public/help/no/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/pl/wiki_syntax_detailed_markdown.html b/public/help/pl/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/pl/wiki_syntax_detailed_markdown.html
      +++ b/public/help/pl/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/pl/wiki_syntax_detailed_textile.html b/public/help/pl/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/pl/wiki_syntax_detailed_textile.html
      +++ b/public/help/pl/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/pl/wiki_syntax_markdown.html b/public/help/pl/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/pl/wiki_syntax_markdown.html
      +++ b/public/help/pl/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/pl/wiki_syntax_textile.html b/public/help/pl/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/pl/wiki_syntax_textile.html
      +++ b/public/help/pl/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/pt-br/wiki_syntax_detailed_markdown.html b/public/help/pt-br/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/pt-br/wiki_syntax_detailed_markdown.html
      +++ b/public/help/pt-br/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/pt-br/wiki_syntax_detailed_textile.html b/public/help/pt-br/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/pt-br/wiki_syntax_detailed_textile.html
      +++ b/public/help/pt-br/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/pt-br/wiki_syntax_markdown.html b/public/help/pt-br/wiki_syntax_markdown.html
      index 68bcba222..dc2b41894 100644
      --- a/public/help/pt-br/wiki_syntax_markdown.html
      +++ b/public/help/pt-br/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Formatação Wiki
      -
      +
       
       
       
      diff --git a/public/help/pt-br/wiki_syntax_textile.html b/public/help/pt-br/wiki_syntax_textile.html
      index 5b64e9246..71732a035 100644
      --- a/public/help/pt-br/wiki_syntax_textile.html
      +++ b/public/help/pt-br/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Formatação Wiki
      -
      +
       
       
       
      diff --git a/public/help/pt/wiki_syntax_detailed_markdown.html b/public/help/pt/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/pt/wiki_syntax_detailed_markdown.html
      +++ b/public/help/pt/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/pt/wiki_syntax_detailed_textile.html b/public/help/pt/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/pt/wiki_syntax_detailed_textile.html
      +++ b/public/help/pt/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/pt/wiki_syntax_markdown.html b/public/help/pt/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/pt/wiki_syntax_markdown.html
      +++ b/public/help/pt/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/pt/wiki_syntax_textile.html b/public/help/pt/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/pt/wiki_syntax_textile.html
      +++ b/public/help/pt/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/ro/wiki_syntax_detailed_markdown.html b/public/help/ro/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/ro/wiki_syntax_detailed_markdown.html
      +++ b/public/help/ro/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/ro/wiki_syntax_detailed_textile.html b/public/help/ro/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/ro/wiki_syntax_detailed_textile.html
      +++ b/public/help/ro/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/ro/wiki_syntax_markdown.html b/public/help/ro/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/ro/wiki_syntax_markdown.html
      +++ b/public/help/ro/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/ro/wiki_syntax_textile.html b/public/help/ro/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/ro/wiki_syntax_textile.html
      +++ b/public/help/ro/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/ru/wiki_syntax_detailed_markdown.html b/public/help/ru/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/ru/wiki_syntax_detailed_markdown.html
      +++ b/public/help/ru/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/ru/wiki_syntax_detailed_textile.html b/public/help/ru/wiki_syntax_detailed_textile.html
      index 989b3343b..32cdfadd1 100644
      --- a/public/help/ru/wiki_syntax_detailed_textile.html
      +++ b/public/help/ru/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       Форматирование Wiki Redmine
       
      -
      +
       
       
       
      @@ -178,6 +152,15 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +

    Исключения:

    @@ -187,7 +170,7 @@

    Внешние ссылки

    -

    HTTP и почтовые адреса автоматически транслируются в ссылки:

    +

    URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

     http://www.redmine.org, someone@foo.bar
    diff --git a/public/help/ru/wiki_syntax_markdown.html b/public/help/ru/wiki_syntax_markdown.html
    index c6f6eff5a..39f8a3c26 100644
    --- a/public/help/ru/wiki_syntax_markdown.html
    +++ b/public/help/ru/wiki_syntax_markdown.html
    @@ -2,21 +2,8 @@
     
     
     
    -
     Wiki formatting
    -
    +
     
     
     
    diff --git a/public/help/ru/wiki_syntax_textile.html b/public/help/ru/wiki_syntax_textile.html
    index cca33f04d..aa980d897 100644
    --- a/public/help/ru/wiki_syntax_textile.html
    +++ b/public/help/ru/wiki_syntax_textile.html
    @@ -2,21 +2,8 @@
     
     
     
    -
     Форматирование Wiki
    -
    +
     
     
     
    diff --git a/public/help/sk/wiki_syntax_detailed_markdown.html b/public/help/sk/wiki_syntax_detailed_markdown.html
    index b42394882..cc4eb7025 100644
    --- a/public/help/sk/wiki_syntax_detailed_markdown.html
    +++ b/public/help/sk/wiki_syntax_detailed_markdown.html
    @@ -3,33 +3,7 @@
     
     RedmineWikiFormatting (Markdown)
     
    -
    +
     
     
     
    @@ -162,6 +136,16 @@
                 
             
     
    +        
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/sk/wiki_syntax_detailed_textile.html b/public/help/sk/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/sk/wiki_syntax_detailed_textile.html
      +++ b/public/help/sk/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/sk/wiki_syntax_markdown.html b/public/help/sk/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/sk/wiki_syntax_markdown.html
      +++ b/public/help/sk/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/sk/wiki_syntax_textile.html b/public/help/sk/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/sk/wiki_syntax_textile.html
      +++ b/public/help/sk/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/sl/wiki_syntax_detailed_markdown.html b/public/help/sl/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/sl/wiki_syntax_detailed_markdown.html
      +++ b/public/help/sl/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/sl/wiki_syntax_detailed_textile.html b/public/help/sl/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/sl/wiki_syntax_detailed_textile.html
      +++ b/public/help/sl/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/sl/wiki_syntax_markdown.html b/public/help/sl/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/sl/wiki_syntax_markdown.html
      +++ b/public/help/sl/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/sl/wiki_syntax_textile.html b/public/help/sl/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/sl/wiki_syntax_textile.html
      +++ b/public/help/sl/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/sq/wiki_syntax_detailed_markdown.html b/public/help/sq/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/sq/wiki_syntax_detailed_markdown.html
      +++ b/public/help/sq/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/sq/wiki_syntax_detailed_textile.html b/public/help/sq/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/sq/wiki_syntax_detailed_textile.html
      +++ b/public/help/sq/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/sq/wiki_syntax_markdown.html b/public/help/sq/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/sq/wiki_syntax_markdown.html
      +++ b/public/help/sq/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/sq/wiki_syntax_textile.html b/public/help/sq/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/sq/wiki_syntax_textile.html
      +++ b/public/help/sq/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/sr-yu/wiki_syntax_detailed_markdown.html b/public/help/sr-yu/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/sr-yu/wiki_syntax_detailed_markdown.html
      +++ b/public/help/sr-yu/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/sr-yu/wiki_syntax_detailed_textile.html b/public/help/sr-yu/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/sr-yu/wiki_syntax_detailed_textile.html
      +++ b/public/help/sr-yu/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/sr-yu/wiki_syntax_markdown.html b/public/help/sr-yu/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/sr-yu/wiki_syntax_markdown.html
      +++ b/public/help/sr-yu/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/sr-yu/wiki_syntax_textile.html b/public/help/sr-yu/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/sr-yu/wiki_syntax_textile.html
      +++ b/public/help/sr-yu/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/sr/wiki_syntax_detailed_markdown.html b/public/help/sr/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/sr/wiki_syntax_detailed_markdown.html
      +++ b/public/help/sr/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/sr/wiki_syntax_detailed_textile.html b/public/help/sr/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/sr/wiki_syntax_detailed_textile.html
      +++ b/public/help/sr/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/sr/wiki_syntax_markdown.html b/public/help/sr/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/sr/wiki_syntax_markdown.html
      +++ b/public/help/sr/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/sr/wiki_syntax_textile.html b/public/help/sr/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/sr/wiki_syntax_textile.html
      +++ b/public/help/sr/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/sv/wiki_syntax_detailed_markdown.html b/public/help/sv/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/sv/wiki_syntax_detailed_markdown.html
      +++ b/public/help/sv/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/sv/wiki_syntax_detailed_textile.html b/public/help/sv/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/sv/wiki_syntax_detailed_textile.html
      +++ b/public/help/sv/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/sv/wiki_syntax_markdown.html b/public/help/sv/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/sv/wiki_syntax_markdown.html
      +++ b/public/help/sv/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/sv/wiki_syntax_textile.html b/public/help/sv/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/sv/wiki_syntax_textile.html
      +++ b/public/help/sv/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/th/wiki_syntax_detailed_markdown.html b/public/help/th/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/th/wiki_syntax_detailed_markdown.html
      +++ b/public/help/th/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/th/wiki_syntax_detailed_textile.html b/public/help/th/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/th/wiki_syntax_detailed_textile.html
      +++ b/public/help/th/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/th/wiki_syntax_markdown.html b/public/help/th/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/th/wiki_syntax_markdown.html
      +++ b/public/help/th/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/th/wiki_syntax_textile.html b/public/help/th/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/th/wiki_syntax_textile.html
      +++ b/public/help/th/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/tr/wiki_syntax_detailed_markdown.html b/public/help/tr/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/tr/wiki_syntax_detailed_markdown.html
      +++ b/public/help/tr/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/tr/wiki_syntax_detailed_textile.html b/public/help/tr/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/tr/wiki_syntax_detailed_textile.html
      +++ b/public/help/tr/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/tr/wiki_syntax_markdown.html b/public/help/tr/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/tr/wiki_syntax_markdown.html
      +++ b/public/help/tr/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/tr/wiki_syntax_textile.html b/public/help/tr/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/tr/wiki_syntax_textile.html
      +++ b/public/help/tr/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/uk/wiki_syntax_detailed_markdown.html b/public/help/uk/wiki_syntax_detailed_markdown.html
      index b42394882..ea01bdb16 100644
      --- a/public/help/uk/wiki_syntax_detailed_markdown.html
      +++ b/public/help/uk/wiki_syntax_detailed_markdown.html
      @@ -1,305 +1,289 @@
       
       
       
      -RedmineWikiFormatting (Markdown)
      +Redmine - форматування вікі сторінок (Markdown)
       
      -
      +
       
       
       
      -

      Wiki formatting (Markdown)

      +

      Форматування вікі сторінок (Markdown)

      -

      Links

      +

      Лінки(посилання)

      -

      Redmine links

      +

      Redmine лінки(посилання)

      -

      Redmine allows hyperlinking between resources (issues, changesets, wiki pages...) from anywhere wiki formatting is used.

      +

      Redmine дозволяє гіперпосилання між ресурсами (завданнями, змінами, вікі-сторінками ...) з будь-якого місця, де використовується вікі-форматування.

        -
      • Link to an issue: #124 (displays #124, link is striked-through if the issue is closed)
      • -
      • Link to an issue note: #124-6, or #124#note-6
      • +
      • Посилання на завдання: #124 (Відображає #124, посилання закресленим, якщо завдання закрите)
      • +
      • Посилання на примітку до завдання: #124-6, or #124#note-6
      -

      Wiki links:

      +

      Вікі посилання:

        -
      • [[Guide]] displays a link to the page named 'Guide': Guide
      • -
      • [[Guide#further-reading]] takes you to the anchor "further-reading". Headings get automatically assigned anchors so that you can refer to them: Guide
      • -
      • [[Guide|User manual]] displays a link to the same page but with a different text: User manual
      • +
      • [[Керівництво користувача]] відображає посилання на сторінку під назвою 'Керівництво користувача': Керівництво користувача
      • +
      • [[Guide#further-reading]] приведе вас до якоря "further-reading". Заголовкам автоматично призначаються якоря так, що Ви можете звернутися до них: Guide
      • +
      • [[Guide|User manual]] відображає посилання на ту ж саму сторінку, але з іншим текстом: User manual
      -

      You can also link to pages of an other project wiki:

      +

      Ви також можете посилатися на сторінки з вікі іншого проекту:

        -
      • [[sandbox:some page]] displays a link to the page named 'Some page' of the Sandbox wiki
      • -
      • [[sandbox:]] displays a link to the Sandbox wiki main page
      • +
      • [[sandbox:some page]] відображає посилання на сторінку з назвою 'Some page' з вікі Sandbox
      • +
      • [[sandbox:]] відображає посилання на головну сторінку вікі Sandbox
      -

      Wiki links are displayed in red if the page doesn't exist yet, eg: Nonexistent page.

      +

      Вікі посилання відображаються червоним кольором, якщо сторінка ще не існує: Неіснуюча сторінка.

      -

      Links to other resources:

      +

      Посилання на інші ресурси:

        -
      • Documents: +
      • Документи:
          -
        • document#17 (link to document with id 17)
        • -
        • document:Greetings (link to the document with title "Greetings")
        • -
        • document:"Some document" (double quotes can be used when document title contains spaces)
        • -
        • sandbox:document:"Some document" (link to a document with title "Some document" in other project "sandbox")
        • +
        • document#17 (посилання на документ з id 17)
        • +
        • document:Привітання (посилання на документ з заголовком "Привітання")
        • +
        • document:"Деякий документ" (подвійні лапки можна використовувати, якщо заголовок документа містить пропуски)
        • +
        • sandbox:document:"Деякий документ" (посилання на документ з заголовком "Деякий документ" з іншого проекту "sandbox")
        -
      • Versions: +
      • Версії:
          -
        • version#3 (link to version with id 3)
        • -
        • version:1.0.0 (link to version named "1.0.0")
        • +
        • version#3 (посилання на версію з id 3)
        • +
        • version:1.0.0 (посилання на версію з назвою "1.0.0")
        • version:"1.0 beta 2"
        • -
        • sandbox:version:1.0.0 (link to version "1.0.0" in the project "sandbox")
        • +
        • sandbox:version:1.0.0 (посилання на версію "1.0.0" з проекту "sandbox")
        -
      • Attachments: +
      • Вкладенні файли:
          -
        • attachment:file.zip (link to the attachment of the current object named file.zip)
        • -
        • For now, attachments of the current object can be referenced only (if you're on an issue, it's possible to reference attachments of this issue only)
        • +
        • attachment:file.zip (посилання на вкладенний файл з ім'ям file.zip)
        • +
        • На даний момент можливо посилатись тільки на вкладення з поточного об'єкту (якщо ви працюєте з завданням, ви можете посилатись тільки на вкладення поточного завдання)
        -
      • Changesets: +
      • Зміни:
          -
        • r758 (link to a changeset)
        • -
        • commit:c6f4d0fd (link to a changeset with a non-numeric hash)
        • -
        • svn1|r758 (link to a changeset of a specific repository, for projects with multiple repositories)
        • -
        • commit:hg|c6f4d0fd (link to a changeset with a non-numeric hash of a specific repository)
        • -
        • sandbox:r758 (link to a changeset of another project)
        • -
        • sandbox:commit:c6f4d0fd (link to a changeset with a non-numeric hash of another project)
        • +
        • r758 (посилання на зміни)
        • +
        • commit:c6f4d0fd (посилання на зміни з нечисловим хешем)
        • +
        • svn1|r758 (посилання на зміни вказаного сховища(репозиторія), для проектів з декількома сховищами(репозиторіями))
        • +
        • commit:hg|c6f4d0fd (посилання на зміни з нечисловим хешем вказаного репозиторія)
        • +
        • sandbox:r758 (посилання на зміни іншого проекту)
        • +
        • sandbox:commit:c6f4d0fd (посилання на зміни з нечисловим іншого проекту)
        -
      • Repository files: +
      • Файли у сховищах(репозиторіях):
          -
        • source:some/file (link to the file located at /some/file in the project's repository)
        • -
        • source:some/file@52 (link to the file's revision 52)
        • -
        • source:some/file#L120 (link to line 120 of the file)
        • -
        • source:some/file@52#L120 (link to line 120 of the file's revision 52)
        • -
        • source:"some file@52#L120" (use double quotes when the URL contains spaces
        • -
        • export:some/file (force the download of the file)
        • -
        • source:svn1|some/file (link to a file of a specific repository, for projects with multiple repositories)
        • -
        • sandbox:source:some/file (link to the file located at /some/file in the repository of the project "sandbox")
        • -
        • sandbox:export:some/file (force the download of the file)
        • +
        • source:some/file (посилання на файл за шляхом /some/file у сховищі проекту)
        • +
        • source:some/file@52 (посилання на file з ревізії 52)
        • +
        • source:some/file#L120 (посилання на рядок 120 з file)
        • +
        • source:some/file@52#L120 (посилання на рядок 120 з file ревізії 52)
        • +
        • source:"some file@52#L120" (використовуте подвійні лапки у випадках, коли URL містить пропуски
        • +
        • export:some/file (примусове завантаження файлу file)
        • +
        • source:svn1|some/file (посилання на file вказаного сховища, для проектів в яких використовується декілька сховищь)
        • +
        • sandbox:source:some/file (посилання на файл за шляхом /some/file з сховища проекту "sandbox")
        • +
        • sandbox:export:some/file (примусове завантаження файлу file)
        -
      • Forums: +
      • Форуми:
          -
        • forum#1 (link to forum with id 1
        • -
        • forum:Support (link to forum named Support)
        • -
        • forum:"Technical Support" (use double quotes if forum name contains spaces)
        • +
        • forum#1 (посилання на форум з id 1
        • +
        • forum:Support (посилання на форум з назвою Support)
        • +
        • forum:"Технічна підтримка" (використовуте подвійні лапки у випадках, коли назва форуму містить пропуски)
        -
      • Forum messages: +
      • Повідомленя на форумах:
          -
        • message#1218 (link to message with id 1218)
        • +
        • message#1218 (посилання на повідомлення з id 1218)
        -
      • Projects: +
      • Проекти:
          -
        • project#3 (link to project with id 3)
        • -
        • project:some-project (link to project with name or slug of "some-project")
        • -
        • project:"Some Project" (use double quotes for project name containing spaces)
        • -
        -
      • -
      - -
        -
      • News: -
          -
        • news#2 (link to news item with id 2)
        • -
        • news:Greetings (link to news item named "Greetings")
        • -
        • news:"First Release" (use double quotes if news item name contains spaces)
        • +
        • project#3 (посилання на проект з id 3)
        • +
        • project:some-project (посилання на проект з назвою або ідентифікатором "some-project")
        • +
        • project:"Some Project" (використовуте подвійні лапки у випадках, коли назва проекту містить пропуски))
      -

      Escaping:

      +
        +
      • Новини: +
          +
        • news#2 (посилання на новину з id 2)
        • +
        • news:Greetings (посилання на новину з заголовком "Greetings")
        • +
        • news:"First Release" (використовуте подвійні лапки у випадках, коли назва новини містить пропуски)
        • +
        +
      • +
        -
      • You can prevent Redmine links from being parsed by preceding them with an exclamation mark: !
      • +
      • Users: +
          +
        • user#2 (link to user with id 2)
        • +
        • user:jsmith (Link to user with login jsmith)
        • +
        • @jsmith (Link to user with login jsmith)
        • +
        +
      • +
      + +

      Запобігання перетворенню(escaping):

      + +
        +
      • Ви можете запобігти, щоб Redmine перетворював посилання, поставивши перед посиланням знак оклику: !
      -

      External links

      +

      Зовнішні посилання

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
       
      -

      displays: http://www.redmine.org,

      +

      відображаються як: http://www.redmine.org,

      -

      If you want to display a specific text instead of the URL, you can use the standard markdown syntax:

      +

      Якщо ви хочете, відобразити текст замість URL, ви можете використовувати стандартний markdown синтаксис:

       [Redmine web site](http://www.redmine.org)
       
      -

      displays: Redmine web site

      +

      відображається як: Redmine web site

      -

      Text formatting

      +

      Форматування тексту

      -

      For things such as headlines, bold, tables, lists, Redmine supports Markdown syntax. See http://daringfireball.net/projects/markdown/syntax for information on using any of these features. A few samples are included below, but the engine is capable of much more of that.

      +

      Для таких речей як: заголовки, жирний текст, таблиці, списки, Redmine підтримує Markdown синтаксис. Перегляньте http://daringfireball.net/projects/markdown/syntax для отримання інформації як цим користуватись. Нижче наводиться декілька прикладів, але можливості Markdown набагато більщі ніж у наведених прикладах.

      -

      Font style

      +

      Стиль шрифту

      -* **bold**
      -* *Italic*
      -* ***bold italic***
      -* ~~strike-through~~
      +* **Жирний**
      +* *Курсив*
      +* ***Жирний курсив***
      +* ~~Закреслений~~
       
      -

      Display:

      +

      Відображення:

        -
      • bold
      • -
      • italic
      • -
      • bold italic
      • -
      • strike-through
      • +
      • Жирний
      • +
      • Курсив
      • +
      • Жирний курсив
      • +
      • Закреслений
      -

      Inline images

      +

      Вбудовані(inline) зображення

        -
      • ![](image_url) displays an image located at image_url (markdown syntax)
      • -
      • If you have an image attached to your wiki page, it can be displayed inline using its filename: ![](attached_image)
      • +
      • ![](image_url) виводить зображення, розташоване за адресою image_url (markdown синтаксис)
      • +
      • ![](attached_image) зображення яке додане до вашої сторінки вікі, може бути відображено, з використанням ім'я файлу
      -

      Headings

      +

      Заголовоки

      -# Heading
      -## Subheading
      -### Subsubheading
      +# Заголовок
      +## Підзаголовок
      +### Підзаголовок
       
      -

      Redmine assigns an anchor to each of those headings thus you can link to them with "#Heading", "#Subheading" and so forth.

      +

      Redmine призначає якір кожному з цих заголовків, таким чином, ви можете посилатись на них з "#Заголовок", "#Підзаголовок" і так далі.

      -

      Blockquotes

      +

      Цитати

      -

      Start the paragraph with >

      +

      Почніть параграф з >

      -> Rails is a full-stack framework for developing database-backed web applications according to the Model-View-Control pattern.
      -To go live, all you need to add is a database and a web server.
      +> Redmine — серверний веб-додаток з відкритим кодом для управління проектами та відстежування помилок. До системи входить календар-планувальник та діаграми Ганта
      +для візуального представлення ходу робіт за проектом та строків виконання.
       
      -

      Display:

      +

      Відображається:

      -

      Rails is a full-stack framework for developing database-backed web applications according to the Model-View-Control pattern.
      To go live, all you need to add is a database and a web server.

      +

      Redmine — серверний веб-додаток з відкритим кодом для управління проектами та відстежування помилок. До системи входить календар-планувальник та діаграми Ганта
      для візуального представлення ходу робіт за проектом та строків виконання.

      -

      Table of content

      +

      Таблиці змісту сторінки

       {{toc}} => left aligned toc
       {{>toc}} => right aligned toc
       
      -

      Horizontal Rule

      +

      Горизонтальна лінія

       ---
       
      -

      Macros

      +

      Макроси

      -

      Redmine has the following builtin macros:

      +

      Redmine має наступні вбудовані макроси:

      hello_world
      -

      Sample macro.

      +

      Приклад макросу.

      macro_list
      -

      Displays a list of all available macros, including description if available.

      +

      Відображає список всіх доступних макросів, в тому числі опис, якщо такий є.

      child_pages
      -

      Displays a list of child pages. With no argument, it displays the child pages of the current wiki page. Examples:

      -
      {{child_pages}} -- can be used from a wiki page only
      -{{child_pages(depth=2)}} -- display 2 levels nesting only
      +

      Відображає список дочірніх сторінок. Без аргументів, він відображає дочірні сторінки поточної сторінки вікі. Приклад:

      +
      {{child_pages}} -- може бути використаний тільки на вікі-сторінці
      +{{child_pages(depth=2)}} -- відображає тільки 2 рівня вкладень
      include
      -

      Include a wiki page. Example:

      +

      Вставити вікі-сторінку. Приклад:

      {{include(Foo)}}
      -

      or to include a page of a specific project wiki:

      +

      або вставити сторінку з конкретного проекту вікі:

      {{include(projectname:Foo)}}
      collapse
      -

      Inserts of collapsed block of text. Example:

      -
      {{collapse(View details...)
      -This is a block of text that is collapsed by default.
      -It can be expanded by clicking a link.
      +      

      Втавте блок тексту, який має з'являтись при натисканні на "Детальніше...". Приклад:

      +
      {{collapse(Детальніше...)
      +Це блок тексту прихований по замовчуванню.
      +Його можливо показати натиснувши на посилання
       }}
      thumbnail
      -

      Displays a clickable thumbnail of an attached image. Examples:

      +

      Відображає інтерактивні мініатюри вкладеного зображення. Приклади:

      {{thumbnail(image.png)}}
       {{thumbnail(image.png, size=300, title=Thumbnail)}}

      -

      Code highlighting

      +

      Підсвітка синтаксису коду

      -

      Default code highlightment relies on CodeRay, a fast syntax highlighting library written completely in Ruby. It currently supports c, clojure, cpp (c++, cplusplus), css, delphi (pascal), diff (patch), erb (eruby, rhtml), go, groovy, haml, html (xhtml), java, javascript (ecmascript, ecma_script, java_script, js), json, lua, php, python, ruby (irb), sass, sql, taskpaper, text (plain, plaintext), xml and yaml (yml) languages, where the names inside parentheses are aliases.

      +

      За замовчуванням підсвічування коду використовує CodeRay, швидка бібліотека для підсвітки синтаксису цілком розроблена на Ruby. На даний час вона підтримує синтаксис: c, clojure, cpp (c++, cplusplus), css, delphi (pascal), diff (patch), erb (eruby, rhtml), go, groovy, haml, html (xhtml), java, javascript (ecmascript, ecma_script, java_script, js), json, lua, php, python, ruby (irb), sass, sql, taskpaper, text (plain, plaintext), xml and yaml (yml) мови, де імена в дужках є псевдонімами.

      -

      You can highlight code at any place that supports wiki formatting using this syntax (note that the language name or alias is case-insensitive):

      +

      Ви можете виділити підсвіткою код в будь-якому місці, яке підтримує вікі-форматування, використовуючи наступний синтаксис (зверніть увагу, що назва мови або псевдонім не чутливі до регістру):

       ~~~ ruby
      @@ -307,7 +291,7 @@ It can be expanded by clicking a link.
       ~~~
       
      -

      Example:

      +

      Приклад:

      # The Greeter class
       class Greeter
      diff --git a/public/help/uk/wiki_syntax_detailed_textile.html b/public/help/uk/wiki_syntax_detailed_textile.html
      index 09f3210ef..1331bad51 100644
      --- a/public/help/uk/wiki_syntax_detailed_textile.html
      +++ b/public/help/uk/wiki_syntax_detailed_textile.html
      @@ -1,309 +1,293 @@
       
       
       
      -RedmineWikiFormatting
      +Redmine - форматування вікі сторінок
       
      -
      +
       
       
       
      -

      Wiki formatting

      +

      Форматування вікі сторінок

      -

      Links

      +

      Лінки(посилання)

      -

      Redmine links

      +

      Redmine лінки(посилання)

      -

      Redmine allows hyperlinking between resources (issues, changesets, wiki pages...) from anywhere wiki formatting is used.

      +

      Redmine дозволяє гіперпосилання між ресурсами (завданнями, змінами, вікі-сторінками ...) з будь-якого місця, де використовується вікі-форматування.

        -
      • Link to an issue: #124 (displays #124, link is striked-through if the issue is closed)
      • -
      • Link to an issue note: #124-6, or #124#note-6
      • +
      • Посилання на завдання: #124 (відображає #124, посилання закресленим, якщо завдання закрите)
      • +
      • Посилання на примітку до завдання: #124-6, or #124#note-6
      -

      Wiki links:

      +

      Вікі посилання:

        -
      • [[Guide]] displays a link to the page named 'Guide': Guide
      • -
      • [[Guide#further-reading]] takes you to the anchor "further-reading". Headings get automatically assigned anchors so that you can refer to them: Guide
      • -
      • [[Guide|User manual]] displays a link to the same page but with a different text: User manual
      • +
      • [[Керівництво користувача]] відображає посилання на сторінку під назвою 'Керівництво користувача': Керівництво користувача
      • +
      • [[Guide#further-reading]] приведе вас до якоря "further-reading". Заголовкам автоматично призначаються якоря так, що Ви можете звернутися до них: Guide
      • +
      • [[Guide|User manual]] відображає посилання на ту ж саму сторінку, але з іншим текстом: User manual
      -

      You can also link to pages of an other project wiki:

      +

      Ви також можете посилатися на сторінки з вікі іншого проекту:

        -
      • [[sandbox:some page]] displays a link to the page named 'Some page' of the Sandbox wiki
      • -
      • [[sandbox:]] displays a link to the Sandbox wiki main page
      • +
      • [[sandbox:some page]] відображає посилання на сторінку з назвою 'Some page' з вікі Sandbox
      • +
      • [[sandbox:]] відображає посилання на головну сторінку вікі Sandbox
      -

      Wiki links are displayed in red if the page doesn't exist yet, eg: Nonexistent page.

      +

      Вікі посилання відображаються червоним кольором, якщо сторінка ще не існує: Неіснуюча сторінка.

      -

      Links to other resources:

      +

      Посилання на інші ресурси:

        -
      • Documents: +
      • Документи:
          -
        • document#17 (link to document with id 17)
        • -
        • document:Greetings (link to the document with title "Greetings")
        • -
        • document:"Some document" (double quotes can be used when document title contains spaces)
        • -
        • sandbox:document:"Some document" (link to a document with title "Some document" in other project "sandbox")
        • +
        • document#17 (посилання на документ з id 17)
        • +
        • document:Привітання (посилання на документ з заголовком "Привітання")
        • +
        • document:"Деякий документ" (подвійні лапки можна використовувати, якщо заголовок документа містить пропуски)
        • +
        • sandbox:document:"Деякий документ" (посилання на документ з заголовком "Деякий документ" з іншого проекту "sandbox")
        -
      • Versions: +
      • Версії:
          -
        • version#3 (link to version with id 3)
        • -
        • version:1.0.0 (link to version named "1.0.0")
        • +
        • version#3 (посилання на версію з id 3)
        • +
        • version:1.0.0 (посилання на версію з назвою "1.0.0")
        • version:"1.0 beta 2"
        • -
        • sandbox:version:1.0.0 (link to version "1.0.0" in the project "sandbox")
        • +
        • sandbox:version:1.0.0 (посилання на версію "1.0.0" з проекту "sandbox")
        -
      • Attachments: +
      • Вкладенні файли:
          -
        • attachment:file.zip (link to the attachment of the current object named file.zip)
        • -
        • For now, attachments of the current object can be referenced only (if you're on an issue, it's possible to reference attachments of this issue only)
        • +
        • attachment:file.zip (посилання на вкладенний файл з ім'ям file.zip)
        • +
        • На даний момент можливо посилатись тільки на вкладення з поточного об'єкту (якщо ви працюєте з завданням, ви можете посилатись тільки на вкладення поточного завдання)
        -
      • Changesets: +
      • Зміни:
          -
        • r758 (link to a changeset)
        • -
        • commit:c6f4d0fd (link to a changeset with a non-numeric hash)
        • -
        • svn1|r758 (link to a changeset of a specific repository, for projects with multiple repositories)
        • -
        • commit:hg|c6f4d0fd (link to a changeset with a non-numeric hash of a specific repository)
        • -
        • sandbox:r758 (link to a changeset of another project)
        • -
        • sandbox:commit:c6f4d0fd (link to a changeset with a non-numeric hash of another project)
        • +
        • r758 (посилання на зміни)
        • +
        • commit:c6f4d0fd (посилання на зміни з нечисловим хешем)
        • +
        • svn1|r758 (посилання на зміни вказаного сховища(репозиторія), для проектів з декількома сховищами(репозиторіями))
        • +
        • commit:hg|c6f4d0fd (посилання на зміни з нечисловим хешем вказаного репозиторія)
        • +
        • sandbox:r758 (посилання на зміни іншого проекту)
        • +
        • sandbox:commit:c6f4d0fd (посилання на зміни з нечисловим іншого проекту)
        -
      • Repository files: +
      • Файли у сховищах(репозиторіях):
          -
        • source:some/file (link to the file located at /some/file in the project's repository)
        • -
        • source:some/file@52 (link to the file's revision 52)
        • -
        • source:some/file#L120 (link to line 120 of the file)
        • -
        • source:some/file@52#L120 (link to line 120 of the file's revision 52)
        • -
        • source:"some file@52#L120" (use double quotes when the URL contains spaces
        • -
        • export:some/file (force the download of the file)
        • -
        • source:svn1|some/file (link to a file of a specific repository, for projects with multiple repositories)
        • -
        • sandbox:source:some/file (link to the file located at /some/file in the repository of the project "sandbox")
        • -
        • sandbox:export:some/file (force the download of the file)
        • +
        • source:some/file (посилання на файл за шляхом /some/file у сховищі проекту)
        • +
        • source:some/file@52 (посилання на file з ревізії 52)
        • +
        • source:some/file#L120 (посилання на рядок 120 з file)
        • +
        • source:some/file@52#L120 (посилання на рядок 120 з file ревізії 52)
        • +
        • source:"some file@52#L120" (використовуте подвійні лапки у випадках, коли URL містить пропуски
        • +
        • export:some/file (примусове завантаження файлу file)
        • +
        • source:svn1|some/file (посилання на file вказаного сховища, для проектів в яких використовується декілька сховищь)
        • +
        • sandbox:source:some/file (посилання на файл за шляхом /some/file з сховища проекту "sandbox")
        • +
        • sandbox:export:some/file (примусове завантаження файлу file)
        -
      • Forums: +
      • Форуми:
          -
        • forum#1 (link to forum with id 1
        • -
        • forum:Support (link to forum named Support)
        • -
        • forum:"Technical Support" (use double quotes if forum name contains spaces)
        • +
        • forum#1 (посилання на форум з id 1
        • +
        • forum:Support (посилання на форум з назвою Support)
        • +
        • forum:"Технічна підтримка" (використовуте подвійні лапки у випадках, коли назва форуму містить пропуски)
        -
      • Forum messages: +
      • Повідомленя на форумах:
          -
        • message#1218 (link to message with id 1218)
        • +
        • message#1218 (посилання на повідомлення з id 1218)
        -
      • Projects: +
      • Проекти:
          -
        • project#3 (link to project with id 3)
        • -
        • project:some-project (link to project with name or slug of "some-project")
        • -
        • project:"Some Project" (use double quotes for project name containing spaces)
        • +
        • project#3 (посилання на проект з id 3)
        • +
        • project:some-project (посилання на проект з назвою або ідентифікатором "some-project")
        • +
        • project:"Some Project" (використовуте подвійні лапки у випадках, коли назва проекту містить пропуски))
        -
      • News: +
      • Новини:
          -
        • news#2 (link to news item with id 2)
        • -
        • news:Greetings (link to news item named "Greetings")
        • -
        • news:"First Release" (use double quotes if news item name contains spaces)
        • +
        • news#2 (посилання на новину з id 2)
        • +
        • news:Greetings (посилання на новину з заголовком "Greetings")
        • +
        • news:"First Release" (використовуте подвійні лапки у випадках, коли назва новини містить пропуски)
      -

      Escaping:

      +
        +
      • Users: +
          +
        • user#2 (link to user with id 2)
        • +
        • user:jsmith (Link to user with login jsmith)
        • +
        • @jsmith (Link to user with login jsmith)
        • +
        +
      • +
      + +

      Запобігання перетворенню(escaping):

        -
      • You can prevent Redmine links from being parsed by preceding them with an exclamation mark: !
      • +
      • Ви можете запобігти, щоб Redmine перетворював посилання, поставивши перед посиланням знак оклику: !
      -

      External links

      +

      Зовнішні посилання

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
       
      -

      displays: http://www.redmine.org,

      +

      відображається як: http://www.redmine.org,

      -

      If you want to display a specific text instead of the URL, you can use the standard textile syntax:

      +

      Якщо ви хочете, відобразити текст замість URL, ви можете використовувати дужки:

       "Redmine web site":http://www.redmine.org
       
      -

      displays: Redmine web site

      +

      відображається як: Redmine web site

      -

      Text formatting

      +

      Форматування тексту

      -

      For things such as headlines, bold, tables, lists, Redmine supports Textile syntax. See http://en.wikipedia.org/wiki/Textile_(markup_language) for information on using any of these features. A few samples are included below, but the engine is capable of much more of that.

      +

      Для таких речей як: заголовки, жирний текст, таблиці, списки, Redmine підтримує Textile синтаксис. Перегляньте https://uk.wikipedia.org/wiki/Textile для отримання інформації як цим користуватись. Нижче наводиться декілька прикладів, але можливості Textile набагато більщі ніж у наведених прикладах.

      -

      Font style

      +

      Стиль шрифту

      -* *bold*
      -* _italic_
      -* _*bold italic*_
      -* +underline+
      -* -strike-through-
      +* *Жирний*
      +* _Курсив_
      +* _*Жирний курсив*_
      +* +Підкреслений+
      +* -Закреслений-
       
      -

      Display:

      +

      Відображення:

        -
      • bold
      • -
      • italic
      • -
      • bold italic
      • -
      • underline
      • -
      • strike-through
      • +
      • Жирний
      • +
      • Курсив
      • +
      • Жирний курсив
      • +
      • Підкреслений
      • +
      • Закреслений
      -

      Inline images

      +

      Вбудовані(inline) зображення

        -
      • !image_url! displays an image located at image_url (textile syntax)
      • -
      • !>image_url! right floating image
      • -
      • If you have an image attached to your wiki page, it can be displayed inline using its filename: !attached_image.png!
      • +
      • !image_url! виводить зображення, розташоване за адресою image_url (textile syntax)
      • +
      • !>image_url! зображення відображається з права(right floating)
      • +
      • !attached_image.png! зображення яке додане до вашої сторінки вікі, може бути відображено, з використанням ім'я файлу
      -

      Headings

      +

      Заголовки

      -h1. Heading
      -h2. Subheading
      -h3. Subsubheading
      +h1. Заголовок
      +h2. Підзаголовок
      +h3. Підзаголовок
       
      -

      Redmine assigns an anchor to each of those headings thus you can link to them with "#Heading", "#Subheading" and so forth.

      +

      Redmine призначає якір кожному з цих заголовків, таким чином, ви можете посилатись на них з "#Заголовок", "#Підзаголовок" і так далі.

      -

      Paragraphs

      +

      Параграфи

       p>. right aligned
       p=. centered
       
      -

      This is a centered paragraph.

      +

      Це центрований абзац.

      -

      Blockquotes

      +

      Цитати

      -

      Start the paragraph with bq.

      +

      Почніть параграф з bq.

      -bq. Rails is a full-stack framework for developing database-backed web applications according to the Model-View-Control pattern.
      -To go live, all you need to add is a database and a web server.
      +bq. Redmine — серверний веб-додаток з відкритим кодом для управління проектами та відстежування помилок. До системи входить календар-планувальник та діаграми Ганта
      +для візуального представлення ходу робіт за проектом та строків виконання.
       
      -

      Display:

      +

      Відображається:

      -

      Rails is a full-stack framework for developing database-backed web applications according to the Model-View-Control pattern.
      To go live, all you need to add is a database and a web server.

      +

      Redmine — серверний веб-додаток з відкритим кодом для управління проектами та відстежування помилок. До системи входить календар-планувальник та діаграми Ганта
      для візуального представлення ходу робіт за проектом та строків виконання.

      -

      Table of content

      +

      Таблиці змісту сторінки

       {{toc}} => left aligned toc
       {{>toc}} => right aligned toc
       
      -

      Horizontal Rule

      +

      Горизонтальна лінія

       ---
       
      -

      Macros

      +

      Макроси

      -

      Redmine has the following builtin macros:

      +

      Redmine має наступні вбудовані макроси:

      hello_world
      -

      Sample macro.

      +

      Приклад макросу.

      macro_list
      -

      Displays a list of all available macros, including description if available.

      +

      Відображає список всіх доступних макросів, в тому числі опис, якщо такий є.

      child_pages
      -

      Displays a list of child pages. With no argument, it displays the child pages of the current wiki page. Examples:

      -
      {{child_pages}} -- can be used from a wiki page only
      -{{child_pages(depth=2)}} -- display 2 levels nesting only
      +

      Відображає список дочірніх сторінок. Без аргументів, він відображає дочірні сторінки поточної сторінки вікі. Приклад:

      +
      {{child_pages}} -- може бути використаний тільки на вікі-сторінці
      +{{child_pages(depth=2)}} -- відображає тільки 2 рівня вкладень
      include
      -

      Include a wiki page. Example:

      +

      Вставити вікі-сторінку. Приклад:

      {{include(Foo)}}
      -

      or to include a page of a specific project wiki:

      +

      або вставити сторінку з конкретного проекту вікі:

      {{include(projectname:Foo)}}
      collapse
      -

      Inserts of collapsed block of text. Example:

      -
      {{collapse(View details...)
      -This is a block of text that is collapsed by default.
      -It can be expanded by clicking a link.
      +      

      Втавте блок тексту, який має з'являтись при натисканні на "Детальніше...". Приклад:

      +
      {{collapse(Детальніше...)
      +Це блок тексту прихований по замовчуванню.
      +Його можливо показати натиснувши на посилання
       }}
      thumbnail
      -

      Displays a clickable thumbnail of an attached image. Examples:

      +

      Відображає інтерактивні мініатюри вкладеного зображення. Приклади:

      {{thumbnail(image.png)}}
       {{thumbnail(image.png, size=300, title=Thumbnail)}}

      -

      Code highlighting

      +

      Підсвітка синтаксису коду

      -

      Default code highlightment relies on CodeRay, a fast syntax highlighting library written completely in Ruby. It currently supports c, clojure, cpp (c++, cplusplus), css, delphi (pascal), diff (patch), erb (eruby, rhtml), go, groovy, haml, html (xhtml), java, javascript (ecmascript, ecma_script, java_script, js), json, lua, php, python, ruby (irb), sass, sql, taskpaper, text (plain, plaintext), xml and yaml (yml) languages, where the names inside parentheses are aliases.

      +

      За замовчуванням підсвічування коду використовує CodeRay, швидка бібліотека для підсвітки синтаксису цілком розроблена на Ruby. На даний час вона підтримує синтаксис: c, clojure, cpp (c++, cplusplus), css, delphi (pascal), diff (patch), erb (eruby, rhtml), go, groovy, haml, html (xhtml), java, javascript (ecmascript, ecma_script, java_script, js), json, lua, php, python, ruby (irb), sass, sql, taskpaper, text (plain, plaintext), xml and yaml (yml) мови, де імена в дужках є псевдонімами.

      -

      You can highlight code at any place that supports wiki formatting using this syntax (note that the language name or alias is case-insensitive):

      +

      Ви можете виділити підсвіткою код в будь-якому місці, яке підтримує вікі-форматування, використовуючи наступний синтаксис (зверніть увагу, що назва мови або псевдонім не чутливі до регістру):

       <pre><code class="ruby">
      @@ -311,7 +295,7 @@ It can be expanded by clicking a link.
       </code></pre>
       
      -

      Example:

      +

      Приклад:

      # The Greeter class
       class Greeter
      diff --git a/public/help/uk/wiki_syntax_markdown.html b/public/help/uk/wiki_syntax_markdown.html
      index c6f6eff5a..af138db46 100644
      --- a/public/help/uk/wiki_syntax_markdown.html
      +++ b/public/help/uk/wiki_syntax_markdown.html
      @@ -2,64 +2,52 @@
       
       
       
      -
      -Wiki formatting
      -
      +Вікі синтаксис (Markdown)
      +
       
       
       
      -

      Wiki Syntax Quick Reference (Markdown)

      +

      Вікі синтаксис швидка підказка (Markdown)

      - - - - - - + + + + + - - - + + + - - - - + + + + - + - - - + + + - + - + @@ -76,7 +64,7 @@ table.sample th, table.sample td { border: solid 1px #bbb; padding: 4px; height:
      Font Styles
      Strong**Strong**Strong
      Italic*Italic*Italic
      Deleted~~Deleted~~Deleted
      Inline Code`Inline Code`Inline Code
      Preformatted text~~~
       lines
       of code
      ~~~
      +
      Cтилі шрифтів
      Жирний**Жирний**Жирний
      Курсив*Курсив*Курсив
      Закреслений~~Закреслений~~Закреслений
      Інлайн код`Інлайн код`Інлайн код
      Попередньо відформатований текст~~~
       Попередньо
       відформатований
       текст
      ~~~
      - lines
      - of code
      + Попередньо
      + відформатований
      + текст
       
      Lists
      Unordered list* Item 1
        * Sub
      * Item 2
      • Item 1
        • Sub
      • Item 2
      Ordered list1. Item 1
         1. Sub
      2. Item 2
      1. Item 1
        1. Sub
      2. Item 2
      Списки
      Ненумерованний список* Пункт
        * Підпункт
      * Пункт
      • Пункт
        • Підпункт
      • Пункт
      Нумерований список1. Пункт
         1. Підпункт
      2. Пункт
      1. Пункт
        1. Підпункт
      2. Пункт
      Headings
      Heading 1# Title 1

      Title 1

      Heading 2## Title 2

      Title 2

      Heading 3### Title 3

      Title 3

      Заголовоки
      Заголовок 1# Заголовок 1

      Заголовок 1

      Заголовок 2## Заголовок 2

      Заголовок 2

      Заголовок 3### Заголовок 3

      Заголовок 3

      Links
      Лінки(посилання)
      http://foo.barhttp://foo.bar
      [Foo](http://foo.bar)Foo
      Redmine links
      Link to a Wiki page[[Wiki page]]Wiki page
      Issue #12Issue #12
      Redmine посилання
      Посилання на вікі сторінку[[Вікі сторінка]]Вікі сторінка
      Завдання #12Завдання #12
      Revision r43Revision r43
      commit:f30e13e43f30e13e4
      source:some/filesource:some/file
      Inline images
      Вбудовані(inline) зображення
      Image![](image_url)
      ![](attached_image)
      Tables
      Таблиці
      | A | B | C |
      |---|---|---|
      | A | B | C |
      | D | E | F |
      -

      More Information

      +

      Детальніша інформація

      diff --git a/public/help/uk/wiki_syntax_textile.html b/public/help/uk/wiki_syntax_textile.html index 55a900dcf..a74662499 100644 --- a/public/help/uk/wiki_syntax_textile.html +++ b/public/help/uk/wiki_syntax_textile.html @@ -2,76 +2,64 @@ - -Wiki formatting - +Вікі синтаксис + -

      Wiki Syntax Quick Reference

      +

      Вікі синтаксис швидка підказка

      - - - - - - - - + + + + + + + - - - + + + - - - - + + + + - + - - - + + + - - + + - + - + @@ -79,7 +67,7 @@ table.sample th, table.sample td { border: solid 1px #bbb; padding: 4px; height:
      Font Styles
      Strong*Strong*Strong
      Italic_Italic_Italic
      Underline+Underline+Underline
      Deleted-Deleted-Deleted
      ??Quote??Quote
      Inline Code@Inline Code@Inline Code
      Preformatted text<pre>
       lines
       of code
      </pre>
      +
      Cтилі шрифтів
      Жирний*Жирний*Жирний
      Курсив_Курсив_Курсив
      Підкреслений+Підкреслений+Підкреслений
      Закреслений-Закреслений-Закреслений
      ??Цитування??Цитування
      Інлайн код@Інлайн код@Інлайн код
      Попередньо відформатований текст<pre>
       Попередньо
       відформатований
       текст
      </pre>
      - lines
      - of code
      + Попередньо
      + відформатований
      + текст
       
      Lists
      Unordered list* Item 1
      ** Sub
      * Item 2
      • Item 1
        • Sub
      • Item 2
      Ordered list# Item 1
      ## Sub
      # Item 2
      1. Item 1
        1. Sub
      2. Item 2
      Списки
      Ненумерованний список* Пункт
      ** Підпункт
      * Пункт
      • Пункт
        • Підпункт
      • Пункт
      Нумерований список# Пункт
      ## Підпункт
      # Пункт
      1. Пункт
        1. Підпункт
      2. Пункт
      Headings
      Heading 1h1. Title 1

      Title 1

      Heading 2h2. Title 2

      Title 2

      Heading 3h3. Title 3

      Title 3

      Заголовоки
      Заголовок 1h1. Заголовок 1

      Заголовок 1

      Заголовок 2h2. Заголовок 2

      Заголовок 2

      Заголовок 3h3. Заголовок 3

      Заголовок 3

      Links
      Лінки(посилання)
      http://foo.barhttp://foo.bar
      "Foo":http://foo.barFoo
      Redmine links
      Link to a Wiki page[[Wiki page]]Wiki page
      Issue #12Issue #12
      Redmine посилання
      Посилання на вікі сторінку[[Вікі сторінка]]Вікі сторінка
      Завдання #12Завдання #12
      Revision r43Revision r43
      commit:f30e13e43f30e13e4
      source:some/filesource:some/file
      Inline images
      Image!image_url!
      Вбудоване(inline) зображення
      Зображення!image_url!
      !attached_image!
      Tables
      Таблиці
      |_. A |_. B |_. C |
      | A | B | C |
      |/2. row span | B | C |
      |\2. col span |
      |_. A |_. B |_. C |
      | A | B | C |
      |/2. об. рядки | B | C |
      |\2. об. колонки |
      - - + +
      ABC
      ABC
      row spanBC
      col span
      об'єднані рядкиBC
      об'єднані колонки
      -

      More Information

      +

      Детальніша інформація

      diff --git a/public/help/vi/wiki_syntax_detailed_markdown.html b/public/help/vi/wiki_syntax_detailed_markdown.html index b42394882..cc4eb7025 100644 --- a/public/help/vi/wiki_syntax_detailed_markdown.html +++ b/public/help/vi/wiki_syntax_detailed_markdown.html @@ -3,33 +3,7 @@ RedmineWikiFormatting (Markdown) - + @@ -162,6 +136,16 @@
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/vi/wiki_syntax_detailed_textile.html b/public/help/vi/wiki_syntax_detailed_textile.html
      index 09f3210ef..78074b41d 100644
      --- a/public/help/vi/wiki_syntax_detailed_textile.html
      +++ b/public/help/vi/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -162,7 +146,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/vi/wiki_syntax_markdown.html b/public/help/vi/wiki_syntax_markdown.html
      index c6f6eff5a..39f8a3c26 100644
      --- a/public/help/vi/wiki_syntax_markdown.html
      +++ b/public/help/vi/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/vi/wiki_syntax_textile.html b/public/help/vi/wiki_syntax_textile.html
      index 55a900dcf..6f544d2a0 100644
      --- a/public/help/vi/wiki_syntax_textile.html
      +++ b/public/help/vi/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki formatting
      -
      +
       
       
       
      diff --git a/public/help/wiki_syntax.css b/public/help/wiki_syntax.css
      new file mode 100644
      index 000000000..4430ea006
      --- /dev/null
      +++ b/public/help/wiki_syntax.css
      @@ -0,0 +1,11 @@
      +h1 { font-family: Verdana, sans-serif; font-size: 14px; text-align: center; color: #444; }
      +body { font-family: Verdana, sans-serif; font-size: 12px; color: #444; }
      +table th { padding-top: 1em; }
      +table td { vertical-align: top; background-color: #f5f5f5; height: 2em; vertical-align: middle;}
      +table td code { font-size: 1.2em; }
      +table td h1 { font-size: 1.8em; text-align: left; }
      +table td h2 { font-size: 1.4em; text-align: left; }
      +table td h3 { font-size: 1.2em; text-align: left; }
      +
      +table.sample { border-collapse: collapse; border-spacing: 0; margin: 4px; }
      +table.sample th, table.sample td { border: solid 1px #bbb; padding: 4px; height: 1em; }
      \ No newline at end of file
      diff --git a/public/help/wiki_syntax_detailed.css b/public/help/wiki_syntax_detailed.css
      new file mode 100644
      index 000000000..94fc9f869
      --- /dev/null
      +++ b/public/help/wiki_syntax_detailed.css
      @@ -0,0 +1,25 @@
      +body { font:80% Verdana,Tahoma,Arial,sans-serif; }
      +h1, h2, h3, h4 {  font-family: Trebuchet MS,Georgia,"Times New Roman",serif; }
      +pre, code { font-size:120%; }
      +pre code { font-size:100%; }
      +pre {
      +    margin: 1em 1em 1em 1.6em;
      +    padding: 2px;
      +    background-color: #fafafa;
      +    border: 1px solid #e2e2e2;
      +    width: auto;
      +    overflow-x: auto;
      +    overflow-y: hidden;
      +}
      +a.new { color: #b73535; }
      +
      +.syntaxhl .class { color:#258; font-weight:bold }
      +.syntaxhl .comment  { color:#385; }
      +.syntaxhl .delimiter { color:black }
      +.syntaxhl .function { color:#06B; font-weight:bold }
      +.syntaxhl .inline { background-color: hsla(0,0%,0%,0.07); color: black }
      +.syntaxhl .inline-delimiter { font-weight: bold; color: #666 }
      +.syntaxhl .instance-variable { color:#33B }
      +.syntaxhl .keyword { color:#939; font-weight:bold }
      +.syntaxhl .string .content { color: #46a }
      +.syntaxhl .string .delimiter { color:#46a }
      \ No newline at end of file
      diff --git a/public/help/zh-tw/wiki_syntax_detailed_markdown.html b/public/help/zh-tw/wiki_syntax_detailed_markdown.html
      index c3c2d912f..c716afcad 100644
      --- a/public/help/zh-tw/wiki_syntax_detailed_markdown.html
      +++ b/public/help/zh-tw/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    逸出字元:

      @@ -171,7 +155,7 @@

      外部連結

      -

      HTTP URLs 與電子郵件地址會自動被轉換成可被點擊的連結:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/zh-tw/wiki_syntax_detailed_textile.html b/public/help/zh-tw/wiki_syntax_detailed_textile.html
      index 08ede7ea9..103d46f79 100644
      --- a/public/help/zh-tw/wiki_syntax_detailed_textile.html
      +++ b/public/help/zh-tw/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -153,6 +127,16 @@
                       
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    逸出字元:

      @@ -162,7 +146,7 @@

      外部連結

      -

      HTTP URLs 與電子郵件地址會自動被轉換成可被點擊的連結:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/zh-tw/wiki_syntax_markdown.html b/public/help/zh-tw/wiki_syntax_markdown.html
      index 246af9575..e62c72010 100644
      --- a/public/help/zh-tw/wiki_syntax_markdown.html
      +++ b/public/help/zh-tw/wiki_syntax_markdown.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki 格式設定
      -
      +
       
       
       
      diff --git a/public/help/zh-tw/wiki_syntax_textile.html b/public/help/zh-tw/wiki_syntax_textile.html
      index 409886b87..361c27fa1 100644
      --- a/public/help/zh-tw/wiki_syntax_textile.html
      +++ b/public/help/zh-tw/wiki_syntax_textile.html
      @@ -2,21 +2,8 @@
       
       
       
      -
       Wiki 格式設定
      -
      +
       
       
       
      diff --git a/public/help/zh/wiki_syntax_detailed_markdown.html b/public/help/zh/wiki_syntax_detailed_markdown.html
      index b42394882..cc4eb7025 100644
      --- a/public/help/zh/wiki_syntax_detailed_markdown.html
      +++ b/public/help/zh/wiki_syntax_detailed_markdown.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting (Markdown)
       
      -
      +
       
       
       
      @@ -162,6 +136,16 @@
                   
               
    +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +
    +

    Escaping:

      @@ -171,7 +155,7 @@

      External links

      -

      HTTP URLs and email addresses are automatically turned into clickable links:

      +

      URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

       http://www.redmine.org, someone@foo.bar
      diff --git a/public/help/zh/wiki_syntax_detailed_textile.html b/public/help/zh/wiki_syntax_detailed_textile.html
      index c0ee17947..9d3265709 100644
      --- a/public/help/zh/wiki_syntax_detailed_textile.html
      +++ b/public/help/zh/wiki_syntax_detailed_textile.html
      @@ -3,33 +3,7 @@
       
       RedmineWikiFormatting
       
      -
      +
       
       
       
      @@ -152,6 +126,16 @@
                           
    • news:"First Release" (新闻名称包含空格时,使用双引号来表示)
    + +
      +
    • Users: +
        +
      • user#2 (link to user with id 2)
      • +
      • user:jsmith (Link to user with login jsmith)
      • +
      • @jsmith (Link to user with login jsmith)
      • +
      +
    • +

    转义字符:

    @@ -162,7 +146,7 @@

    外部链接

    -

    HTTP链接和Email地址可以被自动转换成可点击的链接:

    +

    URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

     http://www.redmine.org, someone@foo.bar
    diff --git a/public/help/zh/wiki_syntax_markdown.html b/public/help/zh/wiki_syntax_markdown.html
    index c6f6eff5a..39f8a3c26 100644
    --- a/public/help/zh/wiki_syntax_markdown.html
    +++ b/public/help/zh/wiki_syntax_markdown.html
    @@ -2,21 +2,8 @@
     
     
     
    -
     Wiki formatting
    -
    +
     
     
     
    diff --git a/public/help/zh/wiki_syntax_textile.html b/public/help/zh/wiki_syntax_textile.html
    index c077f7591..595a9f7c7 100644
    --- a/public/help/zh/wiki_syntax_textile.html
    +++ b/public/help/zh/wiki_syntax_textile.html
    @@ -2,21 +2,8 @@
     
     
     
    -
     Wiki formatting
    -
    +
     
     
     
    diff --git a/public/images/download.png b/public/images/download.png
    new file mode 100644
    index 000000000..080a26eae
    Binary files /dev/null and b/public/images/download.png differ
    diff --git a/public/javascripts/application.js b/public/javascripts/application.js
    index a1beea17a..4e1b40ab8 100644
    --- a/public/javascripts/application.js
    +++ b/public/javascripts/application.js
    @@ -1,5 +1,5 @@
     /* Redmine - project management software
    -   Copyright (C) 2006-2016  Jean-Philippe Lang */
    +   Copyright (C) 2006-2017  Jean-Philippe Lang */
     
     /* Fix for CVE-2015-9251, to be removed with JQuery >= 3.0 */
     $.ajaxPrefilter(function (s) {
    @@ -127,6 +127,18 @@ function initFilters() {
     function addFilter(field, operator, values) {
       var fieldId = field.replace('.', '_');
       var tr = $('#tr_'+fieldId);
    +
    +  var filterOptions = availableFilters[field];
    +  if (!filterOptions) return;
    +
    +  if (filterOptions['remote'] && filterOptions['values'] == null) {
    +    $.getJSON(filtersUrl, {'name': field}).done(function(data) {
    +      filterOptions['values'] = data;
    +      addFilter(field, operator, values) ;
    +    });
    +    return;
    +  }
    +
       if (tr.length > 0) {
         tr.show();
       } else {
    @@ -182,6 +194,11 @@ function buildFilterRow(field, operator, values) {
           if ($.isArray(filterValue)) {
             option.val(filterValue[1]).text(filterValue[0]);
             if ($.inArray(filterValue[1], values) > -1) {option.attr('selected', true);}
    +        if (filterValue.length == 3) {
    +          var optgroup = select.find('optgroup').filter(function(){return $(this).attr('label') == filterValue[2]});
    +          if (!optgroup.length) {optgroup = $('').attr('label', filterValue[2]);}
    +          option = optgroup.append(option);
    +        }
           } else {
             option.val(filterValue).text(filterValue);
             if ($.inArray(filterValue, values) > -1) {option.attr('selected', true);}
    @@ -214,8 +231,8 @@ function buildFilterRow(field, operator, values) {
         );
         $('#values_'+fieldId).val(values[0]);
         select = tr.find('td.values select');
    -    for (i = 0; i < allProjects.length; i++) {
    -      var filterValue = allProjects[i];
    +    for (i = 0; i < filterValues.length; i++) {
    +      var filterValue = filterValues[i];
           var option = $('