Commit 11e93ff3 authored by Holger Just's avatar Holger Just

Merge branch 'release-v3.0.0' into stable

parents ad996d78 3ecace90
......@@ -4,6 +4,7 @@
/config/configuration.yml
/config/database.yml
/config/email.yml
/config/setup_load_paths.rb
/config/initializers/session_store.rb
/coverage
/db/*.db
......
# -*- coding: utf-8 -*-
source :rubygems
gem "rails", "2.3.14"
gem "coderay", "~> 0.9.7"
gem "coderay", "~> 1.0.0"
gem "i18n", "~> 0.4.2"
gem "rubytree", "~> 0.5.2", :require => 'tree'
gem "rdoc", ">= 2.4.2"
gem "liquid", "~> 2.3.0"
gem "acts-as-taggable-on", "= 2.1.0"
# Needed only on RUBY_VERSION = 1.8, ruby 1.9+ compatible interpreters should bring their csv
gem "fastercsv", "~> 1.5.0", :platforms => [:ruby_18, :jruby, :mingw_18]
......
......@@ -19,6 +19,10 @@ class AdminController < ApplicationController
include SortHelper
menu_item :projects, :only => [:projects]
menu_item :plugins, :only => [:plugins]
menu_item :info, :only => [:info]
def index
@no_configuration_data = Redmine::DefaultData::Loader::no_data?
end
......
......@@ -65,6 +65,9 @@ class ApplicationController < ActionController::Base
filter_parameter_logging :password
rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token
# FIXME: This doesn't work with Rails >= 3.0 anymore
# Possible workaround: https://github.com/rails/rails/issues/671#issuecomment-1780159
rescue_from ActionController::RoutingError, :with => proc{render_404}
include Redmine::Search::Controller
include Redmine::MenuManager::MenuController
......@@ -75,8 +78,6 @@ class ApplicationController < ActionController::Base
end
def user_setup
# Check the settings cache for each request
Setting.check_cache
# Find the current user
User.current = find_current_user
end
......
......@@ -13,7 +13,8 @@
#++
class AutoCompletesController < ApplicationController
before_filter :find_project
before_filter :find_project, :only => :issues
before_filter :require_admin, :only => :projects
def issues
@issues = []
......@@ -33,6 +34,38 @@ class AutoCompletesController < ApplicationController
render :layout => false
end
def users
if params[:remove_group_members].present?
@group = Group.find(params[:remove_group_members])
@removed_users = @group.users
end
if params[:remove_watchers].present? && params[:klass].present?
watcher_class = params[:klass].constantize
if watcher_class.included_modules.include?(Redmine::Acts::Watchable) # check class is a watching class
@object = watcher_class.find(params[:remove_watchers])
@removed_users = @object.watcher_users
end
end
@removed_users ||= []
if params[:include_groups]
user_finder = Principal
else
user_finder = User
end
@users = user_finder.active.like(params[:q]).find(:all, :limit => 100) - @removed_users
render :layout => false
end
def projects
@principal = Principal.find(params[:id])
@projects = Project.active.like(params[:q]).find(:all, :limit => 100) - @principal.projects
render :layout => false
end
private
def find_project
......
......@@ -44,21 +44,34 @@ class DocumentsController < ApplicationController
def new
@document = @project.documents.build(params[:document])
if request.post? and @document.save
if request.post?
if User.current.allowed_to?(:add_document_watchers, @project) && params[:document]['watcher_user_ids'].present?
@document.watcher_user_ids = params[:document]['watcher_user_ids']
end
if @document.save
attachments = Attachment.attach_files(@document, params[:attachments])
render_attachment_warning_if_needed(@document)
flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'index', :project_id => @project
end
end
end
def edit
@categories = DocumentCategory.all
if request.post? and @document.update_attributes(params[:document])
if request.post?
if User.current.allowed_to?(:add_document_watchers, @project) && params[:document]['watcher_user_ids'].present?
@document.watcher_user_ids = params[:document]['watcher_user_ids']
end
if @document.update_attributes(params[:document])
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'show', :id => @document
end
end
end
def destroy
@document.destroy
......@@ -69,7 +82,12 @@ class DocumentsController < ApplicationController
attachments = Attachment.attach_files(@document, params[:attachments])
render_attachment_warning_if_needed(@document)
Mailer.deliver_attachments_added(attachments[:files]) if attachments.present? && attachments[:files].present? && Setting.notified_events.include?('document_added')
if attachments.present? && attachments[:files].present? && Setting.notified_events.include?('document_added')
# TODO: refactor
attachments.first.container.recipients.each do |recipient|
Mailer.deliver_attachments_added(attachments[:files], recipient)
end
end
redirect_to :action => 'show', :id => @document
end
......
......@@ -42,7 +42,11 @@ class FilesController < ApplicationController
render_attachment_warning_if_needed(container)
if !attachments.empty? && !attachments[:files].blank? && Setting.notified_events.include?('file_added')
Mailer.deliver_attachments_added(attachments[:files])
# TODO: refactor
recipients = attachments[:files].first.container.project.notified_users.select {|user| user.allowed_to?(:view_files, container.project)}.collect {|u| u.mail}
recipients.each do |recipient|
Mailer.deliver_attachments_added(attachments[:files], recipient)
end
end
redirect_to project_files_path(@project)
end
......
......@@ -126,16 +126,19 @@ class GroupsController < ApplicationController
end
end
def autocomplete_for_user
@group = Group.find(params[:id])
@users = User.active.not_in_group(@group).like(params[:q]).all(:limit => 100)
render :layout => false
end
def edit_membership
@group = Group.find(params[:id])
if params[:project_ids] # Multiple memberships, one per project
params[:project_ids].each do |project_id|
@membership = Member.edit_membership(params[:membership_id], (params[:membership]|| {}).merge(:project_id => project_id), @group)
@membership.save if request.post?
end
else # Single membership
@membership = Member.edit_membership(params[:membership_id], params[:membership], @group)
@membership.save if request.post?
end
respond_to do |format|
if @membership.valid?
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' }
......
......@@ -12,6 +12,8 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'diff'
class JournalsController < ApplicationController
before_filter :find_journal, :only => [:edit, :diff]
before_filter :find_issue, :only => [:new]
......@@ -84,6 +86,22 @@ class JournalsController < ApplicationController
end
end
def diff
if valid_field?(params[:field])
from = @journal.changes[params[:field]][0]
to = @journal.changes[params[:field]][1]
@diff = Redmine::Helpers::Diff.new(to, from)
@issue = @journal.journaled
respond_to do |format|
format.html { }
format.js { render :layout => false }
end
else
render_404
end
end
private
def find_journal
......@@ -100,4 +118,9 @@ class JournalsController < ApplicationController
rescue ActiveRecord::RecordNotFound
render_404
end
# Is this a valid field for diff'ing?
def valid_field?(field)
field.to_s.strip == "description"
end
end
......@@ -14,6 +14,7 @@
class LdapAuthSourcesController < AuthSourcesController
menu_item :ldap_authentication, :only => [:index]
protected
def auth_source_class
......
......@@ -110,7 +110,7 @@ class MessagesController < ApplicationController
content = "#{ll(Setting.default_language, :text_user_wrote, user)}\\n> "
content << text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]').gsub('"', '\"').gsub(/(\r?\n|\r\n?)/, "\\n> ") + "\\n\\n"
render(:update) { |page|
page << "$('reply_subject').value = \"#{subject}\";"
page << "$('message_subject').value = \"#{subject}\";"
page.<< "$('message_content').value = \"#{content}\";"
page.show 'reply'
page << "Form.Element.focus('message_content');"
......
......@@ -20,6 +20,7 @@ class QueriesController < ApplicationController
def new
@query = Query.new(params[:query])
@query.project = params[:query_is_for_all] ? nil : @project
@query.display_subprojects = params[:display_subprojects] if params[:display_subprojects].present?
@query.user = User.current
@query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
......
......@@ -72,8 +72,7 @@ class TimeEntryReportsController < ApplicationController
@periods = []
# Date#at_beginning_of_ not supported in Rails 1.2.x
date_from = @from.to_time
# 100 columns max
while date_from <= @to.to_time && @periods.length < 100
while date_from <= @to.to_time
case @columns
when 'year'
@periods << "#{date_from.year}"
......@@ -161,6 +160,9 @@ class TimeEntryReportsController < ApplicationController
@available_criterias = { 'project' => {:sql => "#{TimeEntry.table_name}.project_id",
:klass => Project,
:label => :label_project},
'status' => {:sql => "#{Issue.table_name}.status_id",
:klass => IssueStatus,
:label => :field_status},
'version' => {:sql => "#{Issue.table_name}.fixed_version_id",
:klass => Version,
:label => :label_version},
......
......@@ -197,8 +197,16 @@ class UsersController < ApplicationController
def edit_membership
if params[:project_ids] # Multiple memberships, one per project
params[:project_ids].each do |project_id|
@membership = Member.edit_membership(params[:membership_id], (params[:membership] || {}).merge(:project_id => project_id), @user)
@membership.save if request.post?
end
else # Single membership
@membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
@membership.save if request.post?
end
respond_to do |format|
if @membership.valid?
format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
......
......@@ -16,6 +16,7 @@ class WatchersController < ApplicationController
before_filter :find_project
before_filter :require_login, :check_project_privacy, :only => [:watch, :unwatch]
before_filter :authorize, :only => [:new, :destroy]
before_filter :authorize_access_to_object, :only => [:new, :destroy]
verify :method => :post,
:only => [ :watch, :unwatch ],
......@@ -34,9 +35,12 @@ class WatchersController < ApplicationController
end
def new
@watcher = Watcher.new(params[:watcher])
params[:user_ids].each do |user_id|
@watcher = Watcher.new((params[:watcher] || {}).merge({:user_id => user_id}))
@watcher.watchable = @watched
@watcher.save if request.post?
end if params[:user_ids].present?
respond_to do |format|
format.html { redirect_to :back }
format.js do
......@@ -50,7 +54,7 @@ class WatchersController < ApplicationController
end
def destroy
@watched.set_watcher(User.find(params[:user_id]), false) if request.post?
@watched.set_watcher(Principal.find(params[:user_id]), false) if request.post?
respond_to do |format|
format.html { redirect_to :back }
format.js do
......@@ -94,4 +98,24 @@ private
rescue ::ActionController::RedirectBackError
render :text => (watching ? 'Watcher added.' : 'Watcher removed.'), :layout => true
end
def authorize_access_to_object
permission = ''
case params[:action]
when 'new'
permission << 'add_'
when 'destroy'
permission << 'delete_'
end
# Ends up like: :delete_wiki_page_watchers
permission << "#{@watched.class.name.underscore}_watchers"
if User.current.allowed_to?(permission.to_sym, @project)
return true
else
deny_access
end
end
end
#-- encoding: UTF-8
#-- copyright
# ChiliProject is a project management system.
#
# Copyright (C) 2010-2012 the ChiliProject Team
#
# 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.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
class BaseDrop < Liquid::Drop
def initialize(object)
@object = object unless object.respond_to?(:visible?) && !object.visible?
end
# Defines a Liquid method on the drop that is allowed to call the
# Ruby method directly. Best used for attributes.
#
# Based on Module#liquid_methods
def self.allowed_methods(*allowed_methods)
class_eval do
allowed_methods.each do |sym|
define_method sym do
if @object.respond_to?(:public_send)
@object.public_send(sym) rescue nil
else
@object.send(sym) rescue nil
end
end
end
end
end
end
#-- encoding: UTF-8
#-- copyright
# ChiliProject is a project management system.
#
# Copyright (C) 2010-2012 the ChiliProject Team
#
# 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.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
class IssueDrop < BaseDrop
allowed_methods :id
allowed_methods :subject
allowed_methods :description
allowed_methods :project
allowed_methods :tracker
allowed_methods :status
allowed_methods :due_date
allowed_methods :category
allowed_methods :assigned_to
allowed_methods :priority
allowed_methods :fixed_version
allowed_methods :author
allowed_methods :created_on
allowed_methods :updated_on
allowed_methods :start_date
allowed_methods :done_ratio
allowed_methods :estimated_hours
allowed_methods :parent
def custom_field(name)
return '' unless name.present?
custom_field = IssueCustomField.find_by_name(name.strip)
return '' unless custom_field.present?
custom_value = @object.custom_value_for(custom_field)
if custom_value.present?
return custom_value.value
else
return ''
end
end
# TODO: both required, method_missing for Ruby and before_method for Liquid
# Allows accessing custom fields by their name:
#
# - issue.the_name_of_player => CustomField(:name => "The name Of Player")
#
def before_method(method_sym)
if custom_field_with_matching_name = has_custom_field_with_matching_name?(method_sym)
custom_field(custom_field_with_matching_name.name)
else
super
end
end
# Allows accessing custom fields by their name:
#
# - issue.the_name_of_player => CustomField(:name => "The name Of Player")
#
def method_missing(method_sym, *arguments, &block)
if custom_field_with_matching_name = has_custom_field_with_matching_name?(method_sym)
custom_field(custom_field_with_matching_name.name)
else
super
end
end
private
def has_custom_field_with_matching_name?(method_sym)
custom_field_with_matching_name = @object.available_custom_fields.detect {|custom_field|
custom_field.name.downcase.underscore.gsub(' ','_') == method_sym.to_s
}
end
end
#-- encoding: UTF-8
#-- copyright
# ChiliProject is a project management system.
#
# Copyright (C) 2010-2012 the ChiliProject Team
#
# 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.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
class IssueStatusDrop < BaseDrop
allowed_methods :name
end
#-- encoding: UTF-8
#-- copyright
# ChiliProject is a project management system.
#
# Copyright (C) 2010-2012 the ChiliProject Team
#
# 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.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
class PrincipalDrop < BaseDrop
allowed_methods :name
end
#-- encoding: UTF-8
#-- copyright
# ChiliProject is a project management system.
#
# Copyright (C) 2010-2012 the ChiliProject Team
#
# 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.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
class ProjectDrop < BaseDrop
allowed_methods :name, :identifier
end
#-- encoding: UTF-8
#-- copyright
# ChiliProject is a project management system.
#
# Copyright (C) 2010-2012 the ChiliProject Team
#
# 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.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
class TrackerDrop < BaseDrop
allowed_methods :name
end
#-- encoding: UTF-8
#-- copyright
# ChiliProject is a project management system.
#
# Copyright (C) 2010-2012 the ChiliProject Team
#
# 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.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
class WikiPageDrop < BaseDrop
allowed_methods :title
end
......@@ -16,7 +16,6 @@ require 'forwardable'
require 'cgi'
module ApplicationHelper
include Redmine::WikiFormatting::Macros::Definitions
include Redmine::I18n
include GravatarHelper::PublicMethods
......@@ -224,17 +223,15 @@ module ApplicationHelper
end
# Renders the project quick-jump box
def render_project_jump_box
projects = User.current.memberships.collect(&:project).compact.uniq
def render_project_jump_box(projects = [], html_options = {})
projects ||= User.current.memberships.collect(&:project).compact.uniq
if projects.any?
s = '<select onchange="if (this.value != \'\') { window.location = this.value; }">' +
"<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
'<option value="" disabled="disabled">---</option>'
s << project_tree_options_for_select(projects, :selected => @project) do |p|
# option_tags = content_tag :option, l(:label_jump_to_a_project), :value => ""
option_tags = (content_tag :option, "", :value => "" )
option_tags << project_tree_options_for_select(projects, :selected => @project) do |p|
{ :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
end
s << '</select>'
s
select_tag "", option_tags, html_options.merge({ :onchange => "if (this.value != \'\') { window.location = this.value; }" })
end
end
......@@ -288,7 +285,15 @@ module ApplicationHelper
def principals_check_box_tags(name, principals)
s = ''
principals.sort.each do |principal|
s << "<label>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
s << "<label style='display:block;'>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
end
s
end
def projects_check_box_tags(name, projects)
s = ''
projects.each do |project|
s << "<label>#{ check_box_tag name, project.id, false } #{h project}</label>\n"
end
s
end
......@@ -389,7 +394,9 @@ module ApplicationHelper
end
def page_header_title
if @project.nil? || @project.new_record?
if @page_header_title.present?
h(@page_header_title)
elsif @project.nil? || @project.new_record?
h(Setting.app_title)
else
b = []
......@@ -429,8 +436,8 @@ module ApplicationHelper
css << 'theme-' + theme.name
end
css << 'controller-' + params[:controller]
css << 'action-' + params[:action]
css << 'controller-' + params[:controller] if params[:controller]
css << 'action-' + params[:action] if params[:action]
css.join(' ')
end
......@@ -447,19 +454,51 @@ module ApplicationHelper
case args.size
when 1
obj = options[:object]
text = args.shift
input_text = args.shift
when 2
obj = args.shift
attr = args.shift
text = obj.send(attr).to_s
input_text = obj.send(attr).to_s
else
raise ArgumentError, 'invalid arguments to textilizable'
end
return '' if text.blank?
return '' if input_text.blank?
project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
only_path = options.delete(:only_path) == false ? false : true
text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr) { |macro, args| exec_macro(macro, obj, args) }
begin
text = ChiliProject::Liquid::Legacy.run_macros(input_text)
liquid_template = ChiliProject::Liquid::Template.parse(text)
liquid_variables = get_view_instance_variables_for_liquid
liquid_variables.merge!({'current_user' => User.current})
liquid_variables.merge!({'toc' => '{{toc}}'}) # Pass toc through to replace later
liquid_variables.merge!(ChiliProject::Liquid::Variables.all)
# Pass :view in a register so this view (with helpers) can be used inside of a tag
text = liquid_template.render(liquid_variables, :registers => {:view => self, :object => obj, :attribute => attr})
# Add Liquid errors to the log
if Rails.logger && Rails.logger.debug?
msg = ""
liquid_template.errors.each do |exception|
msg << "[Liquid Error] #{exception.message}\n:\n#{exception.backtrace.join("\n")}"
msg << "\n\n"
end
Rails.logger.debug msg
end
rescue Liquid::SyntaxError => exception
msg = "[Liquid Syntax Error] #{exception.message}"
if Rails.logger && Rails.logger.debug?
log_msg = "#{msg}\n"
log_msg << exception.backtrace.collect{ |str| " #{str}" }.join("\n")
log_msg << "\n\n"
Rails.logger.debug log_msg
end
# Skip Liquid if there is a syntax error
text = content_tag(:div, msg, :class => "flash error")
text << h(input_text)
end
@parsed_headings = []
text = parse_non_pre_blocks(text) do |text|
......@@ -712,7 +751,7 @@ module ApplicationHelper
end
end
TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
TOC_RE = /<p>\{%\s*toc(_right|_left)?\s*%\}<\/p>/i unless const_defined?(:TOC_RE)
# Renders the TOC with given headings
def replace_toc(text, headings)
......@@ -720,10 +759,14 @@ module ApplicationHelper
if headings.empty?
''
else
div_class = 'toc'
div_class << ' right' if $1 == '>'
div_class << ' left' if $1 == '<'
out = "<ul class=\"#{div_class}\"><li>"
toc_class = 'toc'
toc_class << ' right' if $1 == '_right'
toc_class << ' left' if $1 == '_left'
out = "<fieldset class=\"header_collapsible collapsible #{toc_class}\">"
out << "<legend onclick=\"toggleFieldset(this);\"><span>#{l(:label_toc)}</span></legend>"
out << "<div>"
out << "<ul class=\"toc\"><li>"
root = headings.map(&:first).min
current = root
started = false
......@@ -741,6 +784,7 @@ module ApplicationHelper
end
out << '</li></ul>' * (current - root)
out << '</li></ul>'
out << '</div></fieldset>'
end
end
end
......@@ -879,6 +923,12 @@ module ApplicationHelper
# +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
def avatar(user, options = { })
if Setting.gravatar_enabled?
if user.is_a?(Group)
size = options[:size] || 50
size = "#{size}x#{size}" # image_tag uses WxH
options[:class] ||= 'gravatar'
return image_tag("group.png", options.merge(:size => size))
end
options.merge!({:ssl => (defined?(request) && request.ssl?), :default => Setting.gravatar_default})
email = nil
if user.respond_to?(:mail)
......@@ -935,6 +985,72 @@ module ApplicationHelper
end
end
# Expands the current menu item using JavaScript based on the params
def expand_current_menu
current_menu_class =
case
when params[:controller] == "timelog"
"reports"
when params[:controller] == 'projects' && params[:action] == 'changelog'
"reports"
when params[:controller] == 'issues' && ['calendar','gantt'].include?(params[:action])
"reports"
when params[:controller] == 'projects' && params[:action] == 'roadmap'
'roadmap'
when params[:controller] == 'versions' && params[:action] == 'show'
'roadmap'
when params[:controller] == 'projects' && params[:action] == 'settings'
'settings'
when params[:controller] == 'contracts' || params[:controller] == 'deliverables'
'contracts'
else
params[:controller]
end
javascript_tag("jQuery.menu_expand({ menuItem: '.#{current_menu_class}' });")
end
# Menu items for the main top menu
def main_top_menu_items
split_top_menu_into_main_or_more_menus[:main]
end
# Menu items for the more top menu
def more_top_menu_items
split_top_menu_into_main_or_more_menus[:more]
end
def help_menu_item
split_top_menu_into_main_or_more_menus[:help]
end
# Split the :top_menu into separate :main and :more items
def split_top_menu_into_main_or_more_menus
unless @top_menu_split
items_for_main_level = []
items_for_more_level = []
help_menu = nil
menu_items_for(:top_menu) do |item|
if item.name == :home || item.name == :my_page
items_for_main_level << item
elsif item.name == :help
help_menu = item
elsif item.name == :projects
# Remove, present in layout
else
items_for_more_level << item
end
end
@top_menu_split = {
:main => items_for_main_level,
:more => items_for_more_level,
:help => help_menu
}
end
@top_menu_split
end
private
def wiki_helper
......@@ -946,4 +1062,20 @@ module ApplicationHelper
def link_to_content_update(text, url_params = {}, html_options = {})
link_to(text, url_params, html_options)
end
def get_view_instance_variables_for_liquid
internal_variables = %w{
@output_buffer @cookies @helpers @real_format @assigns_added @assigns
@view_paths @controller
}
self.instance_variables.collect(&:to_s).reject do |ivar|
ivar.match(/^@_/) || # Rails "internal" variables: @_foo
ivar.match(/^@template/) ||
internal_variables.include?(ivar)
end.inject({}) do |acc,ivar|
acc[ivar.sub('@','')] = instance_variable_get(ivar)
acc
end
end
end
......@@ -52,13 +52,14 @@ module IssuesHelper
"<strong>#{@cached_label_priority}</strong>: #{h(issue.priority.name)}"
end
# TODO: deprecate and/or remove
def render_issue_subject_with_tree(issue)
s = ''
ancestors = issue.root? ? [] : issue.ancestors.all
ancestors.each do |ancestor|
s << '<div>' + content_tag('p', link_to_issue(ancestor))
s << '<div>' + content_tag('h2', link_to_issue(ancestor))
end
s << '<div>' + content_tag('h3', h(issue.subject))
s << '<div class="subject">' + content_tag('h2', h(issue.subject))
s << '</div>' * (ancestors.size + 1)
s
end
......
......@@ -27,24 +27,34 @@ module JournalsHelper
def render_journal(model, journal, options = {})
return "" if journal.initial?
journal_content = render_journal_details(journal, :label_updated_time_by)
journal_content += render_notes(model, journal, options) unless journal.notes.blank?
content_tag "div", journal_content, { :id => "change-#{journal.id}", :class => journal.css_classes }
journal_classes = journal.css_classes
journal_content = render_journal_details(journal, :label_updated_time_by, model, options)
avatar = avatar(journal.user, :size => "40")
unless avatar.blank?
profile_wrap = content_tag("div", avatar, {:class => "profile-wrap"}, false)
journal_content = profile_wrap + journal_content
journal_classes << " has-avatar"
end
content_tag("div", journal_content, :id => "change-#{journal.id}", :class => journal_classes)
end
# This renders a journal entry wiht a header and details
def render_journal_details(journal, header_label = :label_updated_time_by)
# This renders a journal entry with a header and details
def render_journal_details(journal, header_label = :label_updated_time_by, model=nil, options={})
header = <<-HTML
<h4>
<div style="float:right;">#{link_to "##{journal.anchor}", :anchor => "note-#{journal.anchor}"}</div>
#{avatar(journal.user, :size => "24")}
#{content_tag('a', '', :name => "note-#{journal.anchor}")}
<div class="journal-link" style="float:right;">#{link_to "##{journal.anchor}", :anchor => "note-#{journal.anchor}"}</div>
#{authoring journal.created_at, journal.user, :label => header_label}
#{content_tag('a', '', :name => "note-#{journal.anchor}")}
</h4>
HTML
header << render_notes(model, journal, options) unless journal.notes.blank?
if journal.details.any?
details = content_tag "ul", :class => "details" do
details = content_tag "ul", :class => "journal-attributes details" do
journal.details.collect do |detail|
if d = journal.render_detail(detail)
content_tag("li", d)
......@@ -53,7 +63,7 @@ module JournalsHelper
end
end
content_tag("div", "#{header}#{details}", :id => "change-#{journal.id}", :class => "journal")
content_tag "div", "#{header}#{details}", :class => "journal-details"
end
def render_notes(model, journal, options={})
......
......@@ -84,11 +84,12 @@ module QueriesHelper
end
end
@query.group_by = params[:group_by]
@query.display_subprojects = params[:display_subprojects] if params[:display_subprojects]
@query.column_names = params[:c] || (params[:query] && params[:query][:column_names])
session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names, :display_subprojects => @query.display_subprojects}
else
@query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
@query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
@query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names], :display_subprojects => session[:query][:display_subprojects])
@query.project = @project
end
end
......
......@@ -24,6 +24,7 @@ class Document < ActiveRecord::Base
end)
acts_as_searchable :columns => ['title', "#{table_name}.description"], :include => :project
acts_as_watchable
validates_presence_of :project, :title, :category
validates_length_of :title, :maximum => 60
......@@ -48,4 +49,10 @@ class Document < ActiveRecord::Base
end
@updated_on
end
def recipients
mails = super # from acts_as_event
mails += watcher_recipients
mails.uniq
end
end
......@@ -14,6 +14,10 @@
class DocumentObserver < ActiveRecord::Observer
def after_create(document)
Mailer.deliver_document_added(document) if Setting.notified_events.include?('document_added')
if Setting.notified_events.include?('document_added')
document.recipients.each do |recipient|
Mailer.deliver_document_added(document, recipient)
end
end
end
end
......@@ -22,6 +22,11 @@ class Group < Principal
validates_uniqueness_of :lastname, :case_sensitive => false
validates_length_of :lastname, :maximum => 30
# Returns an array of all of the email addresses of the group's users
def mails
users.collect(&:mail)
end
def to_s
lastname.to_s
end
......
......@@ -103,6 +103,10 @@ class Issue < ActiveRecord::Base
(usr || User.current).allowed_to?(:view_issues, self.project)
end
def to_liquid
IssueDrop.new(self)
end
def after_initialize
if new_record?
# set default values for new records only
......
......@@ -17,7 +17,9 @@ class IssueObserver < ActiveRecord::Observer
def after_create(issue)
if self.send_notification
Mailer.deliver_issue_add(issue) if Setting.notified_events.include?('issue_added')
(issue.recipients + issue.watcher_recipients).uniq.each do |recipient|
Mailer.deliver_issue_add(issue, recipient)
end
end
clear_notification
end
......
......@@ -28,6 +28,10 @@ class IssueStatus < ActiveRecord::Base
IssueStatus.update_all("is_default=#{connection.quoted_false}", ['id <> ?', id]) if self.is_default?
end
def to_liquid
IssueStatusDrop.new(self)
end
# Returns the default status for new issues
def self.default
find(:first, :conditions =>["is_default=?", true])
......
......@@ -27,11 +27,15 @@ class JournalObserver < ActiveRecord::Observer
if journal.initial?
if Setting.notified_events.include?('wiki_content_added')
Mailer.deliver_wiki_content_added(wiki_content)
(wiki_content.recipients + wiki_page.wiki.watcher_recipients).uniq.each do |recipient|
Mailer.deliver_wiki_content_added(wiki_content, recipient)
end
end
else
if Setting.notified_events.include?('wiki_content_updated')
Mailer.deliver_wiki_content_updated(wiki_content)
(wiki_content.recipients + wiki_page.wiki.watcher_recipients + wiki_page.watcher_recipients).uniq.each do |recipient|
Mailer.deliver_wiki_content_updated(wiki_content, recipient)
end
end
end
end
......@@ -43,7 +47,10 @@ class JournalObserver < ActiveRecord::Observer
(Setting.notified_events.include?('issue_note_added') && journal.notes.present?) ||
(Setting.notified_events.include?('issue_status_updated') && journal.new_status.present?) ||
(Setting.notified_events.include?('issue_priority_updated') && journal.new_value_for('priority_id').present?)
Mailer.deliver_issue_edit(journal)
issue = journal.issue
(issue.recipients + issue.watcher_recipients).uniq.each do |recipient|
Mailer.deliver_issue_edit(journal, recipient)
end
end
end
......
......@@ -69,6 +69,7 @@ class MailHandler < ActionMailer::Base
else
# Default behaviour, emails from unknown users are ignored
logger.info "MailHandler: ignoring email from unknown user [#{sender_email}]" if logger && logger.info
Mailer.deliver_mail_handler_unauthorized_action(user, email.subject.to_s, :to => sender_email) if Setting.mail_handler_confirmation_on_failure
return false
end
end
......@@ -102,12 +103,15 @@ class MailHandler < ActionMailer::Base
rescue ActiveRecord::RecordInvalid => e
# TODO: send a email to the user
logger.error e.message if logger
Mailer.deliver_mail_handler_missing_information(user, email.subject.to_s, e.message) if Setting.mail_handler_confirmation_on_failure
false
rescue MissingInformation => e
logger.error "MailHandler: missing information from #{user}: #{e.message}" if logger
Mailer.deliver_mail_handler_missing_information(user, email.subject.to_s, e.message) if Setting.mail_handler_confirmation_on_failure
false
rescue UnauthorizedAction => e
logger.error "MailHandler: unauthorized attempt from #{user}" if logger
Mailer.deliver_mail_handler_unauthorized_action(user, email.subject.to_s) if Setting.mail_handler_confirmation_on_failure
false
end
......@@ -141,6 +145,7 @@ class MailHandler < ActionMailer::Base
issue.save!
add_attachments(issue)
logger.info "MailHandler: issue ##{issue.id} created by #{user}" if logger && logger.info
Mailer.deliver_mail_handler_confirmation(issue, user, issue.subject) if Setting.mail_handler_confirmation_on_success
issue
end
......@@ -162,6 +167,7 @@ class MailHandler < ActionMailer::Base
add_attachments(issue)
issue.save!
logger.info "MailHandler: issue ##{issue.id} updated by #{user}" if logger && logger.info
Mailer.deliver_mail_handler_confirmation(issue.last_journal, user, email.subject) if Setting.mail_handler_confirmation_on_success
issue.last_journal
end
......@@ -190,6 +196,7 @@ class MailHandler < ActionMailer::Base
reply.board = message.board
message.children << reply
add_attachments(reply)
Mailer.deliver_mail_handler_confirmation(message, user, reply.subject) if Setting.mail_handler_confirmation_on_success
reply
else
logger.info "MailHandler: ignoring reply from [#{sender_email}] to a locked topic" if logger && logger.info
......
This diff is collapsed.
......@@ -14,6 +14,13 @@
class MessageObserver < ActiveRecord::Observer
def after_create(message)
Mailer.deliver_message_posted(message) if Setting.notified_events.include?('message_posted')
if Setting.notified_events.include?('message_posted')
recipients = message.recipients
recipients += message.root.watcher_recipients
recipients += message.board.watcher_recipients
recipients.uniq.each do |recipient|
Mailer.deliver_message_posted(message, recipient)
end
end
end
end
......@@ -14,6 +14,10 @@
class NewsObserver < ActiveRecord::Observer
def after_create(news)
Mailer.deliver_news_added(news) if Setting.notified_events.include?('news_added')
if Setting.notified_events.include?('news_added')
news.recipients.each do |recipient|
Mailer.deliver_news_added(news, recipient)
end
end
end
end
......@@ -31,6 +31,10 @@ class Principal < ActiveRecord::Base
before_create :set_default_empty_values
def to_liquid
PrincipalDrop.new(self)
end
def name(formatter = nil)
to_s
end
......@@ -44,6 +48,82 @@ class Principal < ActiveRecord::Base
end
end
def active?
true
end
def logged?
true # TODO: should all principals default to logged or not?
end
# Return true if the user is allowed to do the specified action on a specific context
# Action can be:
# * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
# * a permission Symbol (eg. :edit_project)
# Context can be:
# * a project : returns true if user is allowed to do the specified action on this project
# * a group of projects : returns true if user is allowed on every project
# * nil with options[:global] set : check if user has at least one role allowed for this action,
# or falls back to Non Member / Anonymous permissions depending if the user is logged
def allowed_to?(action, context, options={})
if context && context.is_a?(Project)
# No action allowed on archived projects
return false unless context.active?
# No action allowed on disabled modules
return false unless context.allows_to?(action)
# Admin users are authorized for anything else
return true if admin?
roles = roles_for_project(context)
return false unless roles
roles.detect {|role| (context.is_public? || role.member?) && role.allowed_to?(action)}
elsif context && context.is_a?(Array)
# Authorize if user is authorized on every element of the array
context.map do |project|
allowed_to?(action,project,options)
end.inject do |memo,allowed|
memo && allowed
end
elsif options[:global]
# Admin users are always authorized
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.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
else
false
end
end
# Is the user allowed to do the specified action on any project?
# See allowed_to? for the actions and valid options.
def allowed_to_globally?(action, options)
allowed_to?(action, nil, options.reverse_merge(:global => true))
end
# Return user's roles for project
def roles_for_project(project)
roles = []
# No role on archived projects
return roles unless project && project.active?
if logged?
# Find project membership
membership = memberships.detect {|m| m.project_id == project.id}
if membership
roles = membership.roles
else
@role_non_member ||= Role.non_member
roles << @role_non_member
end
else
@role_anonymous ||= Role.anonymous
roles << @role_anonymous
end
roles
end
protected
# Make sure we don't try to insert NULL values (see #4632)
......
......@@ -82,6 +82,16 @@ class Project < ActiveRecord::Base
named_scope :active, { :conditions => "#{Project.table_name}.status = #{STATUS_ACTIVE}"}
named_scope :all_public, { :conditions => { :is_public => true } }
named_scope :visible, lambda { { :conditions => Project.visible_by(User.current) } }
named_scope :like, lambda {|q|
s = "%#{q.to_s.strip.downcase}%"
{
:conditions => ["LOWER(name) LIKE ?", s]
}
}
def to_liquid
ProjectDrop.new(self)
end
def initialize(attributes = nil)
super
......@@ -131,6 +141,11 @@ class Project < ActiveRecord::Base
end
end
# Is the project visible to the current user
def visible?
User.current.allowed_to?(:view_project, self)
end
def self.allowed_to_condition(user, permission, options={})
base_statement = "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"
if perm = Redmine::AccessControl.permission(permission)
......@@ -616,7 +631,7 @@ class Project < ActiveRecord::Base
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
ancestors.pop
end
yield project, ancestors.size
yield project, ancestors.size if block_given?
ancestors << project
end
end
......
This diff is collapsed.
#-- encoding: UTF-8
#-- copyright
# ChiliProject is a project management system.
#
# Copyright (C) 2010-2012 the ChiliProject Team
#
# 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.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
class Query::StatementInvalid < ActiveRecord::StatementInvalid
end
#-- encoding: UTF-8
#-- copyright
# ChiliProject is a project management system.
#
# Copyright (C) 2010-2012 the ChiliProject Team
#
# 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.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
class QueryColumn
attr_accessor :name, :sortable, :groupable, :default_order
include Redmine::I18n
def initialize(name, options={})
self.name = name
self.sortable = options[:sortable]
self.groupable = options[:groupable] || false
if groupable == true
self.groupable = name.to_s
end
self.default_order = options[:default_order]
@caption_key = options[:caption] || "field_#{name}"
end
def caption
l(@caption_key)
end
# Returns true if the column is sortable, otherwise false
def sortable?
!sortable.nil?
end
def value(issue)
issue.send name
end
end
#-- encoding: UTF-8
#-- copyright
# ChiliProject is a project management system.
#
# Copyright (C) 2010-2012 the ChiliProject Team
#
# 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.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
class QueryCustomFieldColumn < QueryColumn
def initialize(custom_field)
self.name = "cf_#{custom_field.id}".to_sym
self.sortable = custom_field.order_statement || false
if %w(list date bool int).include?(custom_field.field_format)
self.groupable = custom_field.order_statement
end
self.groupable ||= false
@cf = custom_field
end
def caption
@cf.name
end
def custom_field
@cf
end
def value(issue)
cv = issue.custom_values.detect {|v| v.custom_field_id == @cf.id}
cv && @cf.cast_value(cv.value)
end
end
......@@ -97,13 +97,17 @@ class Setting < ActiveRecord::Base
# Returns the value of the setting named name
def self.[](name)
Marshal.load(Rails.cache.fetch("chiliproject/setting/#{name}") {Marshal.dump(find_or_default(name).value)})
if use_caching?
Marshal.load(Rails.cache.fetch(self.cache_key(name)) {Marshal.dump(find_or_default(name).value)})
else
find_or_default(name).value
end
end
def self.[]=(name, v)
setting = find_or_default(name)
setting.value = (v ? v : "")
Rails.cache.delete "chiliproject/setting/#{name}"
Rails.cache.delete self.cache_key(name)
setting.save
setting.value
end
......@@ -137,23 +141,42 @@ class Setting < ActiveRecord::Base
Object.const_defined?(:OpenID) && self[:openid].to_i > 0
end
# Checks if settings have changed since the values were read
# and clears the cache hash if it's the case
# Called once per request
# Deprecation Warning: This method is no longer available. There is no
# replacement.
def self.check_cache
settings_updated_on = Setting.maximum(:updated_on)
cache_cleared_on = Rails.cache.read('chiliproject/setting-cleared_on')
cache_cleared_on = cache_cleared_on ? Marshal.load(cache_cleared_on) : Time.now
if settings_updated_on && cache_cleared_on <= settings_updated_on
clear_cache
end
# DEPRECATED SINCE 3.0.0beta2
ActiveSupport::Deprecation.warn "The Setting.check_cache method is " +
"deprecated and will be removed in the future. There should be no " +
"replacement for this functionality needed."
end
# Clears all of the Setting caches
def self.clear_cache
Rails.cache.delete_matched( /^chiliproject\/setting\/.+$/ )
Rails.cache.write('chiliproject/setting-cleared_on', Marshal.dump(Time.now))
logger.info 'Settings cache cleared.' if logger
# DEPRECATED SINCE 3.0.0beta2
ActiveSupport::Deprecation.warn "The Setting.clear_cache method is " +
"deprecated and will be removed in the future. There should be no " +
"replacement for this functionality needed. To sweep the whole " +
"cache Rails.cache.clear may be used. To invalidate the Settings " +
"only, you may use Setting.first.try(:touch)"
end
# Temporarily deactivate settings caching in the block scope
def self.uncached
cache_setting = self.use_caching
self.use_caching = false
yield
ensure
self.use_caching = cache_setting
end
# Check if Setting caching should be performed
def self.use_caching?
!Thread.current['chiliproject/settings/do_not_use_caching']
end
# Dis-/En-able Setting caching. This is mainly intended to be used in tests
def self.use_caching=(new_value)
Thread.current['chiliproject/settings/do_not_use_caching'] = !new_value
end
private
......@@ -165,4 +188,8 @@ private
setting = find_by_name(name)
setting ||= new(:name => name, :value => @@available_settings[name]['default']) if @@available_settings.has_key? name
end
def self.cache_key(name)
"chiliproject/setting/#{Setting.maximum(:updated_on).to_i}/#{name}"
end
end
......@@ -35,6 +35,10 @@ class Tracker < ActiveRecord::Base
name <=> tracker.name
end
def to_liquid
TrackerDrop.new(self)
end
def self.all
find(:all, :order => 'position')
end
......
......@@ -345,27 +345,6 @@ class User < Principal
!logged?
end
# Return user's roles for project
def roles_for_project(project)
roles = []
# No role on archived projects
return roles unless project && project.active?
if logged?
# Find project membership
membership = memberships.detect {|m| m.project_id == project.id}
if membership
roles = membership.roles
else
@role_non_member ||= Role.non_member
roles << @role_non_member
end
else
@role_anonymous ||= Role.anonymous
roles << @role_anonymous
end
roles
end
# Return true if the user is a member of project
def member_of?(project)
!roles_for_project(project).detect {|role| role.member?}.nil?
......@@ -388,53 +367,6 @@ class User < Principal
@projects_by_role
end
# Return true if the user is allowed to do the specified action on a specific context
# Action can be:
# * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
# * a permission Symbol (eg. :edit_project)
# Context can be:
# * a project : returns true if user is allowed to do the specified action on this project
# * a group of projects : returns true if user is allowed on every project
# * nil with options[:global] set : check if user has at least one role allowed for this action,
# or falls back to Non Member / Anonymous permissions depending if the user is logged
def allowed_to?(action, context, options={})
if context && context.is_a?(Project)
# No action allowed on archived projects
return false unless context.active?
# No action allowed on disabled modules
return false unless context.allows_to?(action)
# Admin users are authorized for anything else
return true if admin?
roles = roles_for_project(context)
return false unless roles
roles.detect {|role| (context.is_public? || role.member?) && role.allowed_to?(action)}
elsif context && context.is_a?(Array)
# Authorize if user is authorized on every element of the array
context.map do |project|
allowed_to?(action,project,options)
end.inject do |memo,allowed|
memo && allowed
end
elsif options[:global]
# Admin users are always authorized
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.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
else
false
end
end
# Is the user allowed to do the specified action on any project?
# See allowed_to? for the actions and valid options.
def allowed_to_globally?(action, options)
allowed_to?(action, nil, options.reverse_merge(:global => true))
end
safe_attributes 'login',
'firstname',
'lastname',
......
......@@ -14,7 +14,7 @@
class Watcher < ActiveRecord::Base
belongs_to :watchable, :polymorphic => true
belongs_to :user
belongs_to :user, :class_name => 'Principal', :foreign_key => 'user_id'
validates_presence_of :user
validates_uniqueness_of :user_id, :scope => [:watchable_type, :watchable_id]
......
......@@ -53,6 +53,10 @@ class WikiPage < ActiveRecord::Base
end
end
def to_liquid
WikiPageDrop.new(self)
end
def visible?(user=User.current)
!user.nil? && user.allowed_to?(:view_wiki_pages, project)
end
......
<div id="nav-login-content">
<% form_tag({:controller => "account", :action=> "login"}) do %>
<%= hidden_field_tag('back_url', CGI.escape(request.url)) %>
<table>
<tr>
<td><label for="username-pulldown"><%= l(:field_login) %></label></td>
<td><label for="password-pulldown"><%= l(:field_password) %></label></td>
</tr>
<tr>
<td><%= text_field_tag 'username', nil, :tabindex => '1', :id => 'username-pulldown' %></td>
<td><%= password_field_tag 'password', nil, :tabindex => '1', :id => 'password-pulldown' %></td>
<td><input type="submit" name="login" value="<%=l(:button_login)%>" tabindex="1"/></td>
</tr>
</table>
<div id = "optional_login_fields" style = "top = 10px; white-space:nowrap">
<% if Setting.openid? %>
<%= text_field_tag "openid_url", nil, :placeholder => l(:field_identity_url), :tabindex => '1' %>
<% end %>
<% if Setting.autologin? %>
<label for="autologin"><%= check_box_tag 'autologin', 1, false, :tabindex => 1 %> <%= l(:label_stay_logged_in) %></label>
<% end %>
<% if Setting.lost_password? %>
<%= link_to l(:label_password_lost), {:controller => 'account', :action => 'lost_password'}, :tabindex => 1 %>
<% end %>
<% if !User.current.logged? && Setting.self_registration? %>
<%= "|" if Setting.lost_password? %>
<%= link_to l(:label_register), { :controller => 'account', :action => 'register' }, :tabindex => 1 %>
<% end %>
</div>
<% end %>
</div>
......@@ -5,7 +5,7 @@
<table>
<tr>
<td align="right"><label for="username"><%=l(:field_login)%>:</label></td>
<td align="left"><%= text_field_tag 'username', nil, :tabindex => '1' %></td>
<td align="left"><%= text_field_tag 'username', nil, :tabindex => '2' %></td>
</tr>
<tr>
<td align="right"><label for="password"><%=l(:field_password)%>:</label></td>
......
<div id="admin-menu">
<ul>
<%= render_menu :admin_menu %>
</ul>
</div>
<%= render_menu :admin_menu %>
<h2><%=l(:label_administration)%></h2>
<div id="admin-index">
<%= render :partial => 'no_data' if @no_configuration_data %>
<%= render :partial => 'menu' %>
......
<%= projects_check_box_tags 'project_ids[]', @projects %>
\ No newline at end of file
......@@ -71,3 +71,13 @@
<%= auto_discovery_link_tag(:atom, {:format => 'atom', :key => User.current.rss_key}, :title => "#{@project}: #{@board}") %>
<%= stylesheet_link_tag 'scm' %>
<% end %>
<% content_for :sidebar do %>
<% if User.current.allowed_to?(:add_board_watchers, @project) ||
(@board.watchers.present? && User.current.allowed_to?(:view_board_watchers, @project)) %>
<div id="watchers">
<%= render :partial => 'watchers/watchers', :locals => {:watched => @board} %>
</div>
<% end %>
<% end %>
<ul>
<ul class="menu">
<%= call_hook(:view_issues_context_menu_start, {:issues => @issues, :can => @can, :back => @back }) %>
<% if !@issue.nil? -%>
<li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue},
<li class="edit"><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue},
:class => 'icon-edit', :disabled => !@can[:edit] %></li>
<% else %>
<li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id)},
<li class="edit"><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id)},
:class => 'icon-edit', :disabled => !@can[:edit] %></li>
<% end %>
<% if @allowed_statuses.present? %>
<li class="folder">
<a href="#" class="submenu" onclick="return false;"><%= l(:field_status) %></a>
<li class="folder status">
<a href="#" class="context_item" onclick="return false;"><%= l(:field_status) %></a>
<ul>
<% @statuses.each do |s| -%>
<li><%= context_menu_link s.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {:status_id => s}, :back_url => @back}, :method => :post,
:selected => (@issue && s == @issue.status), :disabled => !(@can[:update] && @allowed_statuses.include?(s)) %></li>
<% end -%>
</ul>
<div class="submenu"></div>
</li>
<% end %>
<% unless @trackers.nil? %>
<li class="folder">
<a href="#" class="submenu"><%= l(:field_tracker) %></a>
<li class="folder tracker">
<a href="#" class="context_item"><%= l(:field_tracker) %></a>
<ul>
<% @trackers.each do |t| -%>
<li><%= context_menu_link t.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'tracker_id' => t}, :back_url => @back}, :method => :post,
:selected => (@issue && t == @issue.tracker), :disabled => !@can[:edit] %></li>
<% end -%>
</ul>
<div class="submenu"></div>
</li>
<% end %>
<li class="folder">
<a href="#" class="submenu"><%= l(:field_priority) %></a>
<li class="folder priority">
<a href="#" class="context_item"><%= l(:field_priority) %></a>
<ul>
<% @priorities.each do |p| -%>
<li><%= context_menu_link p.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'priority_id' => p}, :back_url => @back}, :method => :post,
:selected => (@issue && p == @issue.priority), :disabled => (!@can[:edit] || @issues.detect {|i| !i.leaf?}) %></li>
<% end -%>
</ul>
<div class="submenu"></div>
</li>
<% #TODO: allow editing versions when multiple projects %>
<% unless @project.nil? || @project.shared_versions.open.empty? -%>
<li class="folder">
<a href="#" class="submenu"><%= l(:field_fixed_version) %></a>
<li class="folder fixed_version">
<a href="#" class="context_item"><%= l(:field_fixed_version) %></a>
<ul>
<% @project.shared_versions.open.sort.each do |v| -%>
<li><%= context_menu_link format_version_name(v), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'fixed_version_id' => v}, :back_url => @back}, :method => :post,
......@@ -55,11 +58,13 @@
<li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'fixed_version_id' => 'none'}, :back_url => @back}, :method => :post,
:selected => (@issue && @issue.fixed_version.nil?), :disabled => !@can[:update] %></li>
</ul>
<div class="submenu"></div>
</li>
<% end %>
<% if @assignables.present? -%>
<li class="folder">
<a href="#" class="submenu"><%= l(:field_assigned_to) %></a>
<li class="folder assigned">
<a href="#" class="context_item"><%= l(:field_assigned_to) %></a>
<ul>
<% @assignables.each do |u| -%>
<li><%= context_menu_link u.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'assigned_to_id' => u}, :back_url => @back}, :method => :post,
......@@ -68,11 +73,13 @@
<li><%= context_menu_link l(:label_nobody), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'assigned_to_id' => 'none'}, :back_url => @back}, :method => :post,
:selected => (@issue && @issue.assigned_to.nil?), :disabled => !@can[:update] %></li>
</ul>
<div class="submenu"></div>
</li>
<% end %>
<% unless @project.nil? || @project.issue_categories.empty? -%>
<li class="folder">
<a href="#" class="submenu"><%= l(:field_category) %></a>
<a href="#" class="context_item"><%= l(:field_category) %></a>
<ul>
<% @project.issue_categories.each do |u| -%>
<li><%= context_menu_link u.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'category_id' => u}, :back_url => @back}, :method => :post,
......@@ -81,28 +88,30 @@
<li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'category_id' => 'none'}, :back_url => @back}, :method => :post,
:selected => (@issue && @issue.category.nil?), :disabled => !@can[:update] %></li>
</ul>
<div class="submenu"></div>
</li>
<% end -%>
<% if Issue.use_field_for_done_ratio? %>
<li class="folder">
<a href="#" class="submenu"><%= l(:field_done_ratio) %></a>
<li class="folder done_ratio">
<a href="#" class="context_item"><%= l(:field_done_ratio) %></a>
<ul>
<% (0..10).map{|x|x*10}.each do |p| -%>
<li><%= context_menu_link "#{p}%", {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'done_ratio' => p}, :back_url => @back}, :method => :post,
:selected => (@issue && p == @issue.done_ratio), :disabled => (!@can[:edit] || @issues.detect {|i| !i.leaf?}) %></li>
<% end -%>
</ul>
<div class="submenu"></div>
</li>
<% end %>
<% if !@issue.nil? %>
<% if @can[:log_time] -%>
<li><%= context_menu_link l(:button_log_time), {:controller => 'timelog', :action => 'new', :issue_id => @issue},
:class => 'icon-time-add' %></li>
<li class="log_time"><%= context_menu_link l(:button_log_time), {:controller => 'timelog', :action => 'new', :issue_id => @issue},
:class => 'context_item' %></li>
<% end %>
<% if User.current.logged? %>
<li><%= watcher_link(@issue, User.current) %></li>
<li class="watch"><%= watcher_link(@issue, User.current) %></li>
<% end %>
<% end %>
......@@ -110,12 +119,13 @@
<li><%= context_menu_link l(:button_duplicate), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue},
:class => 'icon-duplicate', :disabled => !@can[:copy] %></li>
<% end %>
<li><%= context_menu_link l(:button_copy), new_issue_move_path(:ids => @issues.collect(&:id), :copy_options => {:copy => 't'}),
:class => 'icon-copy', :disabled => !@can[:move] %></li>
<li><%= context_menu_link l(:button_move), new_issue_move_path(:ids => @issues.collect(&:id)),
:class => 'icon-move', :disabled => !@can[:move] %></li>
<li><%= context_menu_link l(:button_delete), {:controller => 'issues', :action => 'destroy', :ids => @issues.collect(&:id), :back_url => @back},
:method => :post, :confirm => l(:text_issues_destroy_confirmation), :class => 'icon-del', :disabled => !@can[:delete] %></li>
<li class="move"><%= context_menu_link l(:button_move), new_issue_move_path(:ids => @issues.collect(&:id)),
:class => 'context_item', :disabled => !@can[:move] %></li>
<li class="copy"><%= context_menu_link l(:button_copy), new_issue_move_path(:ids => @issues.collect(&:id), :copy_options => {:copy => 't'}),
:class => 'context_item' %></li>
<li class="delete"><%= context_menu_link l(:button_delete), {:controller => 'issues', :action => 'destroy', :ids => @issues.collect(&:id)},
:method => :post, :confirm => l(:text_issues_destroy_confirmation), :class => 'context_item', :disabled => !@can[:delete] %></li>
<%= call_hook(:view_issues_context_menu_end, {:issues => @issues, :can => @can, :back => @back }) %>
</ul>
......@@ -9,6 +9,15 @@
<p><label for="document_description"><%=l(:field_description)%></label>
<%= text_area 'document', 'description', :cols => 60, :rows => 15, :class => 'wiki-edit' %></p>
<% if User.current.allowed_to?(:add_document_watchers, @project) -%>
<p id="watchers_form"><label><%= l(:label_document_watchers) %></label>
<% @document.project.users.sort.each do |user| -%>
<label class="floating"><%= check_box_tag 'document[watcher_user_ids][]', user.id, @document.watched_by?(user) %> <%=h user %></label>
<% end -%>
</p>
<% end %>
<!--[eoform:document]-->
</div>
......
......@@ -27,6 +27,16 @@
<% html_title h(@document.title) -%>
<% content_for :sidebar do %>
<% if User.current.allowed_to?(:add_document_watchers, @project) ||
(@document.watchers.present? && User.current.allowed_to?(:view_document_watchers, @project)) %>
<div id="watchers">
<%= render :partial => 'watchers/watchers', :locals => {:watched => @document} %>
</div>
<% end %>
<% end %>
<% content_for :header_tags do %>
<%= stylesheet_link_tag 'scm' %>
<% end %>
......@@ -41,17 +41,5 @@
</div>
<div class="splitcontentright">
<% if projects.any? %>
<fieldset><legend><%=l(:label_project_new)%></legend>
<% remote_form_for(:membership, :url => { :action => 'edit_membership', :id => @group }) do %>
<%= label_tag "membership_project_id", l(:description_choose_project), :class => "hidden-for-sighted" %>
<%= select_tag 'membership[project_id]', options_for_membership_project_select(@group, projects) %>
<p><%= l(:label_role_plural) %>:
<% roles.each do |role| %>
<label><%= check_box_tag 'membership[role_ids][]', role.id %> <%=h role %></label>
<% end %></p>
<p><%= submit_tag l(:button_add) %></p>
<% end %>
</fieldset>
<% end %>
<%= render :partial => 'members/membership_assignment', :locals => {:principal => @group, :projects => projects - @group.projects, :roles => roles } %>
</div>
......@@ -33,7 +33,7 @@
<%= observe_field(:user_search,
:frequency => 0.5,
:update => :users,
:url => { :controller => 'groups', :action => 'autocomplete_for_user', :id => @group },
:url => auto_complete_users_path(:remove_group_members => @group),
:with => 'q')
%>
......
......@@ -13,20 +13,22 @@
}
a.new { color: #b73535; }
.CodeRay .c { color:#666; }
.syntaxhl .line-numbers { padding: 2px 4px 2px 4px; background-color: #eee; margin:0 }
.syntaxhl .comment { color:#666; }
.CodeRay .cl { color:#B06; font-weight:bold }
.CodeRay .dl { color:black }
.CodeRay .fu { color:#06B; font-weight:bold }
.syntaxhl .class { color:#B06; font-weight:bold }
.syntaxhl .delimiter { color:black }
.syntaxhl .function { color:#06B; font-weight:bold }
.CodeRay .il { background: #eee }
.CodeRay .il .idl { font-weight: bold; color: #888 }
.syntaxhl .inline { background: #eee }
.syntaxhl .inline .inline-delimiter { font-weight: bold; color: #888 }
.CodeRay .iv { color:#33B }
.CodeRay .r { color:#080; font-weight:bold }
.syntaxhl .instance-variable { color:#33B }
.syntaxhl .reserved { color:#080; font-weight:bold }
.syntaxhl .string { background-color:#fff0f0; color: #D20; }
.syntaxhl .string .delimiter { color:#710 }
.CodeRay .s { background-color:#fff0f0 }
.CodeRay .s .dl { color:#710 }
<% end %>
<% html_title "Wiki Formatting" %>
......@@ -236,7 +238,7 @@ To go live, all you need to add is a database and a web server.
<h2><a name="13" class="wiki-page"></a>Code highlighting</h2>
<p>Code highlightment relies on <a href="http://coderay.rubychan.de/" class="external">CodeRay</a>, a fast syntax highlighting library written completely in Ruby. It currently supports c, cpp, css, delphi, groovy, html, java, javascript, json, php, python, rhtml, ruby, scheme, sql, xml and yaml languages.</p>
<p>The default code highlighting relies on <a href="http://coderay.rubychan.de/" class="external">CodeRay</a>, a fast syntax highlighting library written completely in Ruby. It currently supports c, cpp, css, delphi, groovy, html, java, javascript, json, php, python, rhtml, ruby, scheme, sql, xml and yaml languages.</p>
<p>You can highlight code in your wiki page using this syntax:</p>
......@@ -248,15 +250,14 @@ To go live, all you need to add is a database and a web server.
<p>Example:</p>
<pre><code class="ruby CodeRay"><span class="no"> 1</span> <span class="c"># The Greeter class</span>
<span class="no"> 2</span> <span class="r">class</span> <span class="cl">Greeter</span>
<span class="no"> 3</span> <span class="r">def</span> <span class="fu">initialize</span>(name)
<span class="no"> 4</span> <span class="iv">@name</span> = name.capitalize
<span class="no"> 5</span> <span class="r">end</span>
<span class="no"> 6</span>
<span class="no"> 7</span> <span class="r">def</span> <span class="fu">salute</span>
<span class="no"> 8</span> puts <span class="s"><span class="dl">"</span><span class="k">Hello </span><span class="il"><span class="idl">#{</span><span class="iv">@name</span><span class="idl">}</span></span><span class="k">!</span><span class="dl">"</span></span>
<span class="no"> 9</span> <span class="r">end</span>
<span class="no"><strong>10</strong></span> <span class="r">end</span>
</code>
<pre><code class="ruby syntaxhl"><span class="line-numbers"> 1</span> <span class="comment"># The Greeter class</span>
<span class="line-numbers"> 2</span> <span class="reserved">class</span> <span class="class">Greeter</span>
<span class="line-numbers"> 3</span> <span class="reserved">def</span> <span class="function">initialize</span>(name)
<span class="line-numbers"> 4</span> <span class="instance-variable">@name</span> = name.capitalize
<span class="line-numbers"> 5</span> <span class="reserved">end</span>
<span class="line-numbers"> 6</span>
<span class="line-numbers"> 7</span> <span class="reserved">def</span> <span class="function">salute</span>
<span class="line-numbers"> 8</span> puts <span class="string"><span class="delimiter">"</span><span class="content">Hello </span><span class="inline"><span class="inline-delimiter">#{</span><span class="instance-variable">@name</span><span class="inline-delimiter">}</span></span><span class="content">!</span><span class="delimiter">"</span></span>
<span class="line-numbers"> 9</span> <span class="reserved">end</span>
<span class="line-numbers"><strong>10</strong></span> <span class="reserved">end</span></code>
</pre>
......@@ -38,7 +38,7 @@
<%= call_hook(:view_issues_edit_notes_bottom, { :issue => @issue, :notes => @notes, :form => f }) %>
</fieldset>
<fieldset id="attachments" class="collapsible collapsed borders"><legend onclick="toggleFieldset(this);"><%=l(:label_attachment_plural)%></legend>
<fieldset id="attachments" class="header_collapsible collapsible collapsed"><legend onclick="toggleFieldset(this);"><%=l(:label_attachment_plural)%></legend>
<div style="display: none;">
<%= render :partial => 'attachments/form' %>
</div>
......
<div class="contextual">
<% if authorize_for('issue_relations', 'new') %>
<%= toggle_link l(:button_add), 'new-relation-form', {:focus => 'relation_issue_to_id'} %>
<% end %>
</div>
<p><strong><%=l(:label_related_issues)%></strong></p>
<p>
<strong><%=l(:label_related_issues)%></strong>
<% if authorize_for('issue_relations', 'new') %>
(<%= toggle_link l(:button_add), 'new-relation-form', {:focus => 'relation_issue_to_id'} %>)
<% end %>
</p>
<% if @relations.present? %>
<table style="width:100%">
......
<h3><%= l(:label_issue_plural) %></h3>
<%= link_to l(:label_issue_view_all), { :controller => 'issues', :action => 'index', :project_id => @project, :set_filter => 1 } %><br />
<% if @project %>
<%= link_to l(:field_summary), :controller => 'reports', :action => 'issue_report', :id => @project %><br />
<% end %>
<%= call_hook(:view_issues_sidebar_issues_bottom) %>
<% if User.current.allowed_to?(:view_calendar, @project, :global => true) %>
<%= link_to(l(:label_calendar), :controller => 'calendars', :action => 'show', :project_id => @project) %><br />
<% end %>
<% if User.current.allowed_to?(:view_gantt, @project, :global => true) %>
<%= link_to(l(:label_gantt), :controller => 'gantts', :action => 'show', :project_id => @project) %><br />
<% end %>
<%= call_hook(:view_issues_sidebar_planning_bottom) %>
<%= render_sidebar_queries %>
......
<div class="title-bar">
<h2><%= @query.new_record? ? l(:label_issue_plural) : h(@query.name) %></h2>
<div class="title-bar-extras">
<div class="contextual">
<% if !@query.new_record? && @query.editable_by?(User.current) %>
<%= link_to l(:button_edit), {:controller => 'queries', :action => 'edit', :id => @query}, :class => 'icon icon-edit' %>
<%= link_to l(:button_delete), {:controller => 'queries', :action => 'destroy', :id => @query}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
<% end %>
<%= render :partial => 'queries/new_issue_button' %>
</div>
<h2><%= @query.new_record? ? l(:label_issue_plural) : h(@query.name) %></h2>
<% html_title(@query.new_record? ? l(:label_issue_plural) : h(@query.name)) %>
<% form_tag({ :controller => 'queries', :action => 'new' }, :id => 'query_form') do %>
<%= hidden_field_tag('project_id', @project.to_param) if @project %>
<div id="query_form_content" class="hide-when-print">
<fieldset id="filters" class="collapsible <%= @query.new_record? ? "" : "collapsed" %>">
<fieldset id="filters" class="header_collapsible collapsible <%= @query.new_record? ? "" : "collapsed" %>">
<legend onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend>
<div style="<%= @query.new_record? ? "" : "display: none;" %>">
<div class="filter-fields" style="<%= @query.new_record? ? "" : "display: none;" %>">
<%= render :partial => 'queries/filters', :locals => {:query => @query} %>
</div>
</fieldset>
<fieldset class="collapsible collapsed">
<fieldset id="column_options" class="header_collapsible collapsible collapsed">
<legend onclick="toggleFieldset(this);"><%= l(:label_options) %></legend>
<div style="display: none;">
<table>
......@@ -29,6 +35,20 @@
<td><label for='group_by'><%= l(:field_group_by) %></label></td>
<td><%= select_tag('group_by', options_for_select([[]] + @query.groupable_columns.collect {|c| [c.caption, c.name.to_s]}, @query.group_by)) %></td>
</tr>
<tr>
<td><%= l(:label_project_plural) %></td>
<td>
<label>
<%= radio_button_tag('display_subprojects', '0', !@query.display_subprojects?) %>
<%= l(:text_current_project) %>
</label>
<br />
<label>
<%= radio_button_tag('display_subprojects', '1', @query.display_subprojects?) %>
<%= l(:label_and_its_subprojects, :value => l(:text_current_project)) %>
</label>
</td>
</tr>
</table>
</div>
</fieldset>
......@@ -55,6 +75,9 @@
</p>
<% end %>
</div><!-- .title-bar-extras -->
</div><!-- .title-bar -->
<%= error_messages_for 'query' %>
<% if @query.valid? %>
<% if @issues.empty? %>
......
<%= render :partial => 'action_menu' %>
<h2><%= h(@issue.tracker.name) %> #<%= h(@issue.id) %><%= call_hook(:view_issues_show_identifier, :issue => @issue) %></h2>
<div class="title-bar" id="upper-title-bar">
<div class="title-bar-actions">
<%= render :partial => 'action_menu' %>
</div>
</div>
<div class="<%= @issue.css_classes %> details">
<%= avatar(@issue.author, :size => "50") %>
<div class="subject">
<%= render_issue_subject_with_tree(@issue) %>
</div>
<h1 class="subject">
<%=h(@issue.subject) %> (<%= h(@issue.tracker.name) + ' #' +@issue.id.to_s %>)
</h1>
<hr />
<p class="author">
<%= avatar(@issue.author, :size => "14") %>
<%= authoring @issue.created_on, @issue.author %>.
<% if @issue.created_on != @issue.updated_on %>
<%= l(:label_updated_time, time_tag(@issue.updated_on)) %>.
<% end %>
</p>
<hr />
<table class="attributes">
<tr>
<div class="meta">
<table class="attributes">
<tr>
<th class="status"><%=l(:field_status)%>:</th><td class="status"><%= h(@issue.status.name) %></td>
<th class="start-date"><%=l(:field_start_date)%>:</th><td class="start-date"><%= format_date(@issue.start_date) %></td>
</tr>
<tr>
</tr>
<tr>
<th class="priority"><%=l(:field_priority)%>:</th><td class="priority"><%= h(@issue.priority.name) %></td>
<th class="due-date"><%=l(:field_due_date)%>:</th><td class="due-date"><%= format_date(@issue.due_date) %></td>
</tr>
<tr>
</tr>
<tr>
<th class="assigned-to"><%=l(:field_assigned_to)%>:</th><td class="assigned-to"><%= avatar(@issue.assigned_to, :size => "14") %><%= @issue.assigned_to ? link_to_user(@issue.assigned_to) : "-" %></td>
<th class="progress"><%=l(:field_done_ratio)%>:</th><td class="progress"><%= progress_bar @issue.done_ratio, :width => '80px', :legend => "#{@issue.done_ratio}%" %></td>
</tr>
<tr>
</tr>
<tr>
<th class="category"><%=l(:field_category)%>:</th><td class="category"><%=h(@issue.category ? @issue.category.name : "-") %></td>
<% if User.current.allowed_to?(:view_time_entries, @project) %>
<th class="spent-time"><%=l(:label_spent_time)%>:</th>
<td class="spent-time"><%= @issue.spent_hours > 0 ? (link_to l_hours(@issue.spent_hours), {:controller => 'timelog', :action => 'index', :project_id => @project, :issue_id => @issue}) : "-" %></td>
<% end %>
</tr>
<tr>
</tr>
<tr>
<th class="fixed-version"><%=l(:field_fixed_version)%>:</th><td class="fixed-version"><%= @issue.fixed_version ? link_to_version(@issue.fixed_version) : "-" %></td>
<% if @issue.estimated_hours %>
<th class="estimated-hours"><%=l(:field_estimated_hours)%>:</th><td class="estimated-hours"><%= l_hours(@issue.estimated_hours) %></td>
<% end %>
</tr>
<%= render_custom_fields_rows(@issue) %>
<%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %>
</table>
</tr>
<%= render_custom_fields_rows(@issue) %>
<%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %>
</table>
</div><!-- .meta -->
<% if @issue.description? || @issue.attachments.any? -%>
<hr />
<% if @issue.description? %>
<hr />
<div class="description">
<div class="contextual">
<%= link_to_remote_if_authorized(l(:button_quote), { :url => {:controller => 'journals', :action => 'new', :id => @issue} }, :class => 'icon icon-comment') %>
</div>
......@@ -56,8 +64,12 @@
<div class="wiki">
<%= textilizable @issue, :description, :attachments => @issue.attachments %>
</div>
</div>
<% end %>
<%= link_to_attachments @issue %>
<% if @issue.attachments.any? -%>
<hr />
<%= link_to_attachments @issue %>
<% end -%>
<%= call_hook(:view_issues_show_description_bottom, :issue => @issue) %>
......@@ -65,10 +77,11 @@
<% if !@issue.leaf? || User.current.allowed_to?(:manage_subtasks, @project) %>
<hr />
<div id="issue_tree">
<div class="contextual">
<%= link_to(l(:button_add), {:controller => 'issues', :action => 'new', :project_id => @project, :issue => {:parent_issue_id => @issue}}) if User.current.allowed_to?(:manage_subtasks, @project) %>
</div>
<p><strong><%=l(:label_subtask_plural)%></strong></p>
<p>
<strong><%=l(:label_subtask_plural)%></strong>
(<%= link_to(l(:button_add), {:controller => 'issues', :action => 'new', :project_id => @project, :issue => {:parent_issue_id => @issue}}) if User.current.allowed_to?(:manage_subtasks, @project) %>)
</p>
<%= render_descendants_tree(@issue) unless @issue.leaf? %>
</div>
<% end %>
......@@ -78,6 +91,7 @@
<div id="relations">
<%= render :partial => 'relations' %>
</div>
<hr />
<% end %>
</div>
......@@ -91,14 +105,19 @@
<% if @journals.present? %>
<div id="history">
<h3><%=l(:label_history)%></h3>
<h3 class="rounded-background"><%=l(:label_history)%></h3>
<%= render :partial => 'history', :locals => { :issue => @issue, :journals => @journals } %>
</div>
<% end %>
<div style="clear: both;"></div>
<%= render :partial => 'action_menu' %>
<div class="title-bar" id="lower-title-bar">
<div class="title-bar-actions">
<%= render :partial => 'action_menu', :locals => {:replace_watcher => 'watcher2' } %>
</div>
</div>
<div style="clear: both;"></div>
<% if authorize_for('issues', 'edit') %>
......
<p><%= authoring @journal.created_at, @journal.user, :label => :label_updated_time_by %></p>
<div class="text-diff">
<%= simple_format_without_paragraph @diff.to_html %>
</div>
<p><%= link_to(l(:button_back), issue_path(@issue)) %></p>
<% html_title "#{@issue.tracker.name} ##{@issue.id}: #{@issue.subject}" %>
<% unless controller_name == 'admin' && action_name == 'index' %>
<% content_for :sidebar do %>
<h3><%=l(:label_administration)%></h3>
<% @page_header_title = l(:label_administration) %>
<% content_for :main_menu do %>
<%= render :partial => 'admin/menu' %>
<% end %>
<% end %>
<%= render :file => "layouts/base" %>
......@@ -7,76 +7,149 @@
<meta name="keywords" content="issue,bug,tracker" />
<%= csrf_meta_tag %>
<%= favicon %>
<%= stylesheet_link_tag 'reset', :media => 'all' %>
<%= stylesheet_link_tag 'smoothness/jquery-ui', :media => 'all' %>
<%= stylesheet_link_tag 'application', :media => 'all' %>
<%= stylesheet_link_tag 'print', :media => 'print' %>
<%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %>
<!--[if lte IE 6]><%= stylesheet_link_tag 'ie6', :media => 'all' %><![endif]-->
<!--[if lte IE 7]><%= stylesheet_link_tag 'ie7', :media => 'all' %><![endif]-->
<!--[if gte IE 8]><![endif]-->
<%= javascript_include_tag 'jquery.min.js' %>
<%= javascript_include_tag 'jquery-ui.min.js' %>
<%= javascript_include_tag 'jquery.menu_expand.js' %>
<%= javascript_tag('jQuery.noConflict();') %>
<%= javascript_heads %>
<%= stylesheet_link_tag 'jstoolbar' %>
<%= heads_for_theme %>
<!--[if IE 6]>
<style type="text/css">
* html body{ width: expression( document.documentElement.clientWidth < 900 ? '900px' : '100%' ); }
body {behavior: url(<%= stylesheet_path "csshover.htc" %>);}
</style>
<![endif]-->
<% heads_for_wiki_formatter %>
<%= call_hook :view_layouts_base_html_head %>
<!-- page specific tags -->
<%= yield :header_tags -%>
</head>
<body class="<%=h body_css_classes %>">
<div id="wrapper">
<div id="wrapper2">
<div id="top-menu">
<div id="account">
<%= render_menu :account_menu -%>
<div id="top-menu">
<div id="header">
<div id="logo"><%= link_to(h(Setting.app_title), home_path) %></div>
<div id="top-menu-items">
<div id="search">
<%= render :partial => 'search/quick_search', :locals => {:search_term => @question} %>
</div>
<%= content_tag('div', "#{l(:label_logged_as)} #{link_to_user(User.current, :format => :username)}", :id => 'loggedas') if User.current.logged? %>
<%= render_menu :top_menu if User.current.logged? || !Setting.login_required? -%>
</div>
<div id="header">
<% if User.current.logged? || !Setting.login_required? %>
<div id="quick-search">
<% form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get ) do %>
<%= hidden_field_tag(controller.default_search_scope, 1, :id => nil) if controller.default_search_scope %>
<label for='q'>
<%= link_to l(:label_search), {:controller => 'search', :action => 'index', :id => @project}, :accesskey => accesskey(:search) %>:
</label>
<%= text_field_tag 'q', @question, :size => 20, :class => 'small', :accesskey => accesskey(:quick_search) %>
<ul id="account-nav">
<% main_top_menu_items.each do |item| %>
<%= render_menu_node(item) %>
<% end %>
<li class="drop-down">
<%= link_to l(:label_project_plural), { :controller => 'projects', :action => 'index' }, :class => "projects" %>
<ul style="display:none;">
<li><%= link_to l(:label_project_all), { :controller => 'projects', :action => 'index' }, :class => "projects separator" %></li>
<%
project_content = ''
project_tree(User.current.projects.all) do |project, level|
name_prefix = (level > 0 ? ('&nbsp;' * 2 * level + '&#187; ') : '')
project_content << content_tag(:li,
link_to(name_prefix + h(project), {:controller => 'projects', :action => 'show', :id => project, :jump => current_menu_item}))
end
%>
<%= project_content %>
</ul>
</li>
<% if more_top_menu_items.present? || User.current.admin? %>
<li class="drop-down" id="more-menu">
<a class="more" href="#"><%= l(:label_more) %></a>
<ul style="display:none;">
<% more_top_menu_items.each do |item| %>
<%= render_menu_node(item) %>
<% end %>
<%# TODO: Extract to helper %>
<% if User.current.admin? %>
<% menu_items_for(:admin_menu) do |item| -%>
<li><%= link_to h(item.caption), item.url, item.html_options %></li>
<% end -%>
<% end %>
<%= render_project_jump_box %>
</ul>
</li>
<% end %>
<li>
<%= render_menu_node(help_menu_item) %>
<% unless User.current.logged? %>
<% if Setting.self_registration? %>
<li>
<%= link_to l(:label_register), { :controller => 'account', :action => 'register' } %>
</li>
<% end %>
<li id="login-menu" class="drop-down last-child">
<%= link_to l(:label_login), {:controller => 'account', :action => 'login'}, :class => 'login' %>
<div id="nav-login" style="display:none;">
<%= render :partial => 'account/login' %>
</div>
</li>
<% else %>
<li class="drop-down last-child">
<%= link_to_user(User.current) %>
<ul style="display:none;">
<% menu_items_for(:account_menu) do |item| %>
<%= render_menu_node(item) %>
<% end %>
</ul>
</li>
<% end %>
</li>
</ul>
<% end %>
</div>
</div>
<div id="breadcrumb">
<%= page_header_title %>
</div>
</div>
<h1><%= page_header_title %></h1>
<% if display_main_menu?(@project) %>
<% main_menu = render_main_menu(@project) %>
<% if (side_displayed = has_content?(:sidebar) || has_content?(:main_menu) || !main_menu.blank?) %>
<% display_sidebar = true %>
<% else %>
<% display_sidebar = false %>
<% end %>
<div id="main" class="<%= side_displayed ? '' : "nosidebar" %>">
<% if (side_displayed) %>
<div id="side-container">
<div id="main-menu">
<%= render_main_menu(@project) %>
<%= main_menu %>
<%= yield :main_menu %>
</div>
<% end %>
</div>
<%= tag('div', {:id => 'main', :class => (has_content?(:sidebar) ? '' : 'nosidebar')}, true) %>
<% if display_sidebar %>
<!-- Sidebar -->
<div id="sidebar">
<%= yield :sidebar %>
<%= call_hook :view_layouts_base_sidebar %>
</div>
<% end %>
</div>
<div id="content">
<%= expand_current_menu %>
<% end %>
<div class="<%= side_displayed ? '' : "nosidebar" %>" id="content">
<%= render_flash_messages %>
<%= yield %>
<%= call_hook :view_layouts_base_content %>
<div style="clear:both;"></div>
<div style="clear:both;">&nbsp;</div>
</div>
</div>
</div>
<div id="ajax-indicator" style="display:none;"><span><%= l(:label_loading) %></span></div>
<div id="footer">
<div id="footer">
<div class="bgl"><div class="bgr">
<%= l(:text_powered_by, :link => link_to(Redmine::Info.app_name, Redmine::Info.url)) %>
</div></div>
</div>
</div>
</div>
<div id="ajax-indicator" style="display:none;"><span><%= l(:label_loading) %></span></div>
<div id="dialog-window" style="display:none;"></div>
</div>
<%= call_hook :view_layouts_base_body_bottom %>
</body>
......
<p><%= l(:text_mail_handler_confirmation_successful) %><br />
<%= auto_link(@url) %></p>
<%= l(:text_mail_handler_confirmation_successful) %>
<%= @url %>
<p><%= l(:label_mail_handler_errors_with_submission) %></p>
<p><%= h(@errors) %></p>
<%= l(:label_mail_handler_errors_with_submission) %>
<%= h(@errors) %>
<% if projects.any? %>
<fieldset><legend><%=l(:label_project_new)%></legend>
<% remote_form_for(:membership, :url => { :action => 'edit_membership', :id => principal }) do %>
<p><%= text_field_tag 'project_search', nil, :size => "40" %></p>
<%= observe_field(:project_search,
:frequency => 0.5,
:update => :projects,
:url => { :controller => 'auto_completes', :action => 'projects', :id => principal },
:with => 'q')
%>
<div id="projects">
<%= principals_check_box_tags 'project_ids[]', projects %>
</div>
<p><%= l(:label_role_plural) %>:
<% roles.each do |role| %>
<label><%= check_box_tag 'membership[role_ids][]', role.id %> <%=h role %></label>
<% end %></p>
<p><%= submit_tag l(:button_add) %></p>
<% end %>
</fieldset>
<% end %>
......@@ -63,4 +63,14 @@
<%= stylesheet_link_tag 'scm' %>
<% end %>
<% content_for :sidebar do %>
<% if User.current.allowed_to?(:add_message_watchers, @project) ||
(@topic.watchers.present? && User.current.allowed_to?(:view_message_watchers, @project)) %>
<div id="watchers">
<%= render :partial => 'watchers/watchers', :locals => {:watched => @topic} %>
</div>
<% end %>
<% end %>
<% html_title h(@topic.subject) %>
......@@ -2,7 +2,7 @@
<%= auto_discovery_link_tag(:atom, {:action => 'index', :format => 'atom', :key => User.current.rss_key}) %>
<% end %>
<div class="contextual">
<div id="project-links" class="contextual">
<%= link_to(l(:label_project_new), {:controller => 'projects', :action => 'new'}, :class => 'icon icon-add') + ' |' if User.current.allowed_to?(:add_project, nil, :global => true) %>
<%= link_to(l(:label_issue_view_all), { :controller => 'issues' }) + ' |' if User.current.allowed_to?(:view_issues, nil, :global => true) %>
<%= link_to(l(:label_overall_spent_time), { :controller => 'time_entries' }) + ' |' if User.current.allowed_to?(:view_time_entries, nil, :global => true) %>
......@@ -13,6 +13,10 @@
<h2><%=l(:label_project_plural)%></h2>
<div class="wiki">
<%= textilizable Setting.welcome_text %>
</div>
<%= call_hook(:view_projects_show_top) %>
<%= render_project_hierarchy(@projects)%>
......
......@@ -13,7 +13,7 @@
<td><%=h(category.assigned_to.name) if category.assigned_to %></td>
<td class="buttons">
<%= link_to_if_authorized l(:button_edit), { :controller => 'issue_categories', :action => 'edit', :id => category }, :class => 'icon icon-edit' %>
<%= link_to_if_authorized l(:button_delete), {:controller => 'issue_categories', :action => 'destroy', :id => category}, :method => :post, :class => 'icon icon-del' %>
<%= link_to_if_authorized l(:button_delete), {:controller => 'issue_categories', :action => 'destroy', :id => category}, :method => :post, :class => 'icon icon-del', :confirm => l(:text_are_you_sure) %>
</td>
</tr>
<% end %>
......
......@@ -22,13 +22,30 @@ function toggle_filter(field) {
if (check_box.checked) {
Element.show("operators_" + field);
Form.Element.enable("operators_" + field);
Form.Element.enable("values_" + field);
toggle_operator(field);
} else {
Element.hide("operators_" + field);
Element.hide("div_values_" + field);
Form.Element.disable("operators_" + field);
Form.Element.disable("values_" + field);
enableValues(field, []);
}
}
function enableValues(field, indexes) {
var f = $$(".values_" + field);
for(var i=0;i<f.length;i++) {
if (indexes.include(i)) {
Form.Element.enable(f[i]);
f[i].up('span').show();
} else {
f[i].value = '';
Form.Element.disable(f[i]);
f[i].up('span').hide();
}
}
if (indexes.length > 0) {
Element.show("div_values_" + field);
} else {
Element.hide("div_values_" + field);
}
}
......@@ -41,16 +58,27 @@ function toggle_operator(field) {
case "w":
case "o":
case "c":
Element.hide("div_values_" + field);
enableValues(field, []);
break;
case "><":
enableValues(field, [0,1]);
break;
case "<t+":
case ">t+":
case "t+":
case ">t-":
case "<t-":
case "t-":
enableValues(field, [2]);
break;
default:
Element.show("div_values_" + field);
enableValues(field, [0]);
break;
}
}
function toggle_multi_select(field) {
select = $('values_' + field);
function toggle_multi_select(el) {
var select = $(el);
if (select.multiple == true) {
select.multiple = false;
} else {
......@@ -92,16 +120,19 @@ Event.observe(document,"dom:loaded", apply_filters_observer);
<div id="div_values_<%= field %>" style="display:none;">
<% case options[:type]
when :list, :list_optional, :list_status, :list_subprojects %>
<select <%= "multiple=true" if query.values_for(field) and query.values_for(field).length > 1 %> name="v[<%= field %>][]" id="values_<%= field %>" class="select-small" style="vertical-align: top;">
<%= options_for_select options[:values], query.values_for(field) %>
</select>
<%= link_to_function image_tag('bullet_toggle_plus.png'), "toggle_multi_select('#{field}');", :style => "vertical-align: bottom;" %>
<span class="span_values_<%= field %>">
<%= select_tag "v[#{field}][]", options_for_select(options[:values], query.values_for(field)), :class => "values_#{field}", :id => "values_#{field}_1", :multiple => (query.values_for(field) && query.values_for(field).length > 1) %>
<%= link_to_function image_tag('bullet_toggle_plus.png'), "toggle_multi_select('values_#{field}_1');", :style => "vertical-align: bottom;" %>
</span>
<% when :date, :date_past %>
<%= text_field_tag "v[#{field}][]", query.values_for(field).try(:first), :id => "values_#{field}", :size => 3, :class => "select-small" %> <%= l(:label_day_plural) %>
<span class="span_values_<%= field %>"><%= text_field_tag "v[#{field}][]", query.value_for(field), :size => 10, :class => "values_#{field}", :id => "values_#{field}_1" %> <%= calendar_for "values_#{field}_1" %></span>
<span class="span_values_<%= field %>"><%= text_field_tag "v[#{field}][]", query.value_for(field, 1), :size => 10, :class => "values_#{field}", :id => "values_#{field}_2" %> <%= calendar_for "values_#{field}_2" %></span>
<span class="span_values_<%= field %>"><%= text_field_tag "v[#{field}][]", query.value_for(field), :size => 3, :class => "values_#{field}" %> <%= l(:label_day_plural) %></span>
<% when :string, :text %>
<%= text_field_tag "v[#{field}][]", query.values_for(field).try(:first), :id => "values_#{field}", :size => 30, :class => "select-small" %>
<span class="span_values_<%= field %>"><%= text_field_tag "v[#{field}][]", query.value_for(field), :class => "values_#{field}", :id => "values_#{field}", :size => 30 %></span>
<% when :integer %>
<%= text_field_tag "v[#{field}][]", query.values_for(field).try(:first), :id => "values_#{field}", :size => 3, :class => "select-small" %>
<span class="span_values_<%= field %>"><%= text_field_tag "v[#{field}][]", query.value_for(field), :class => "values_#{field}", :id => "values_#{field}_1", :size => 3 %></span>
<span class="span_values_<%= field %>"><%= text_field_tag "v[#{field}][]", query.value_for(field, 1), :class => "values_#{field}", :id => "values_#{field}_2", :size => 3 %></span>
<% end %>
</div>
<script type="text/javascript">toggle_filter('<%= field %>');</script>
......
......@@ -16,6 +16,18 @@
<%= check_box_tag 'query_is_for_all', 1, @query.project.nil?,
:disabled => (!@query.new_record? && (@query.project.nil? || (@query.is_public? && !User.current.admin?))) %></p>
<p><label><%= l(:label_project_plural) %></label>
<label style="text-align: left; float: none; margin-left: 0px;font-weight: normal">
<%= radio_button_tag('display_subprojects', '0', !@query.display_subprojects?) %>
<%= l(:text_current_project) %>
</label>
<br />
<label style="text-align: left; float: none; margin-left: 0px;font-weight: normal">
<%= radio_button_tag('display_subprojects', '1', @query.display_subprojects?) %>
<%= l(:label_and_its_subprojects, :value => l(:text_current_project)) %>
</label>
</p>
<p><label for="query_default_columns"><%=l(:label_default_columns)%></label>
<%= check_box_tag 'default_columns', 1, @query.has_default_columns?, :id => 'query_default_columns',
:onclick => 'if (this.checked) {Element.hide("columns")} else {Element.show("columns")}' %></p>
......
<% if @project %>
<div class="new-issue button-large">
<%= link_to_if_authorized(l(:label_issue_new), {:controller => 'issues', :action => 'new', :project_id => @project}, :title => l(:label_issue_new), :class => '') %>
</div>
<% end %>
<%= label_tag("q", l(:label_search), :class => "hidden-for-sighted") %>
<% form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get) do %>
<%= hidden_field_tag(controller.default_search_scope, 1, :id => nil) if controller.default_search_scope %>
<%= text_field_tag 'q', search_term, :size => 20, :class => 'search_field', :placeholder => l(:search_input_placeholder), :accesskey => accesskey(:quick_search) %>
<% end %>
......@@ -5,6 +5,9 @@
<%= setting_text_area :mail_handler_body_delimiters, :rows => 5 %>
<br /><em><%= l(:text_line_separated) %></em>
</p>
<p><%= setting_check_box :mail_handler_confirmation_on_success %></p>
<p><%= setting_check_box :mail_handler_confirmation_on_failure %></p>
</div>
<div class="box tabular settings">
......
......@@ -4,8 +4,9 @@
:onchange => 'if (this.value == "selected") {Element.show("notified-projects")} else {Element.hide("notified-projects")}' %>
</p>
<% content_tag 'div', :id => 'notified-projects', :style => (@user.mail_notification == 'selected' ? '' : 'display:none;') do %>
<p><% @user.projects.each do |project| %>
<label><%= check_box_tag 'notified_project_ids[]', project.id, @user.notified_projects_ids.include?(project.id) %> <%=h project.name %></label><br />
<p><% project_tree(@user.projects.each) do |project,level| %>
<% name_prefix = (level > 0 ? ('&nbsp;' * 2 * level + '&#187; ') : '') %>
<label><%= check_box_tag 'notified_project_ids[]', project.id, @user.notified_projects_ids.include?(project.id) %> <%= name_prefix + h(project.name) %></label><br />
<% end %></p>
<p><em><%= l(:text_user_mail_option) %></em></p>
<% end %>
......
......@@ -47,16 +47,5 @@
</div>
<div class="splitcontentright">
<% if projects.any? %>
<fieldset><legend><%=l(:label_project_new)%></legend>
<% remote_form_for(:membership, :url => { :action => 'edit_membership', :id => @user }) do %>
<%= select_tag 'membership[project_id]', options_for_membership_project_select(@user, projects) %>
<p><%= l(:label_role_plural) %>:
<% roles.each do |role| %>
<label><%= check_box_tag 'membership[role_ids][]', role.id %> <%=h role %></label>
<% end %></p>
<p><%= submit_tag l(:button_add) %></p>
<% end %>
</fieldset>
<% end %>
<%= render :partial => 'members/membership_assignment', :locals => {:principal => @user, :projects => projects - @user.projects, :roles => roles } %>
</div>
......@@ -47,6 +47,7 @@
<% @versions.each do |version| %>
<%= link_to format_version_name(version), "##{version.name}" %><br />
<% end %>
<% end %>
<% html_title(l(:label_roadmap)) %>
......
<div class="contextual">
<%= link_to_remote l(:button_add),
:url => {:controller => 'watchers',
:action => 'new',
:object_type => watched.class.name.underscore,
:object_id => watched} if User.current.allowed_to?(:add_issue_watchers, @project) %>
<%= link_to_function(l(:button_add), "$('new-watcher-form').toggle();") if User.current.allowed_to?("add_#{watched.class.name.underscore}_watchers".to_sym, @project) %>
</div>
<h3><%= l(:label_issue_watchers) %> (<%= watched.watcher_users.size %>)</h3>
<% unless @watcher.nil? %>
<% if User.current.allowed_to?("add_#{watched.class.name.underscore}_watchers".to_sym, @project) %>
<% remote_form_for(:watcher, @watcher,
:url => {:controller => 'watchers',
:action => 'new',
:object_type => watched.class.name.underscore,
:object_id => watched},
:method => :post,
:html => {:id => 'new-watcher-form'}) do |f| %>
<p><%= f.select :user_id, (watched.addable_watcher_users.collect {|m| [m.name, m.id]}), :prompt => "--- #{l(:actionview_instancetag_blank_option)} ---" %>
:html => {:id => 'new-watcher-form', :style => 'display:none;'}) do |f| %>
<% users = Principal.active.find(:all, :limit => 25) - watched.watcher_users %>
<p><%= label_tag "user_search", l(:label_user_search) %><%= text_field_tag 'user_search', nil, :style => "width:98%;" %></p>
<%= observe_field(:user_search,
:frequency => 0.5,
:update => :users,
:url => auto_complete_users_path(:remove_watchers => watched.id, :klass => watched.class, :include_groups => true),
:with => 'q')
%>
<div id="users">
<%= principals_check_box_tags 'user_ids[]', users %>
</div>
<%= submit_tag l(:button_add) %>
<p><%= submit_tag l(:button_add) %>
<%= toggle_link l(:button_cancel), 'new-watcher-form'%></p>
<% end %>
<% end %>
......
......@@ -59,6 +59,14 @@
<% content_for :sidebar do %>
<%= render :partial => 'wiki/sidebar' %>
<% if User.current.allowed_to?(:add_wiki_page_watchers, @project) ||
(@page.watchers.present? && User.current.allowed_to?(:view_wiki_page_watchers, @project)) %>
<div id="watchers">
<%= render :partial => 'watchers/watchers', :locals => {:watched => @page} %>
</div>
<% end %>
<% end %>
<% html_title h(@page.pretty_title) %>
......@@ -52,6 +52,9 @@ Rails::Initializer.run do |config|
# (by default production uses :info, the others :debug)
# config.log_level = :debug
# Liquid drops
config.autoload_paths += %W( #{RAILS_ROOT}/app/drops )
# Enable page/fragment caching by setting a file-based store
# (remember to create the caching directory and make it readable to the application)
# config.action_controller.cache_store = :file_store, "#{RAILS_ROOT}/tmp/cache"
......
......@@ -984,3 +984,16 @@ bg:
description_date_from: Въведете начална дата
label_deleted_custom_field: (изтрито потребителско поле)
field_custom_filter: Custom LDAP filter
text_display_subprojects: Display subprojects
text_current_project: Current project
label_toc: Contents
search_input_placeholder: search ...
setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email
label_mail_handler_confirmation: "Confirmation of email submission: %{subject}"
label_mail_handler_errors_with_submission: "There were errors with your email submission:"
label_document_watchers: Watchers
setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email
label_between: between
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
......@@ -998,3 +998,16 @@ bs:
description_date_from: Enter start date
label_deleted_custom_field: (deleted custom field)
field_custom_filter: Custom LDAP filter
text_display_subprojects: Display subprojects
text_current_project: Current project
label_toc: Contents
search_input_placeholder: search ...
setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email
label_mail_handler_confirmation: "Confirmation of email submission: %{subject}"
label_mail_handler_errors_with_submission: "There were errors with your email submission:"
label_document_watchers: Watchers
setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email
label_between: between
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
......@@ -987,3 +987,16 @@ ca:
description_date_from: Enter start date
label_deleted_custom_field: (deleted custom field)
field_custom_filter: Custom LDAP filter
text_display_subprojects: Display subprojects
text_current_project: Current project
label_toc: Contents
search_input_placeholder: search ...
setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email
label_mail_handler_confirmation: "Confirmation of email submission: %{subject}"
label_mail_handler_errors_with_submission: "There were errors with your email submission:"
label_document_watchers: Watchers
setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email
label_between: between
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
......@@ -1208,3 +1208,16 @@ cs:
description_date_from: Enter start date
label_deleted_custom_field: (deleted custom field)
field_custom_filter: Custom LDAP filter
text_display_subprojects: Display subprojects
text_current_project: Current project
label_toc: Contents
search_input_placeholder: search ...
setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email
label_mail_handler_confirmation: "Confirmation of email submission: %{subject}"
label_mail_handler_errors_with_submission: "There were errors with your email submission:"
label_document_watchers: Watchers
setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email
label_between: between
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
......@@ -1000,3 +1000,16 @@ da:
description_date_from: Enter start date
label_deleted_custom_field: (deleted custom field)
field_custom_filter: Custom LDAP filter
text_display_subprojects: Display subprojects
text_current_project: Current project
label_toc: Contents
search_input_placeholder: search ...
setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email
label_mail_handler_confirmation: "Confirmation of email submission: %{subject}"
label_mail_handler_errors_with_submission: "There were errors with your email submission:"
label_document_watchers: Watchers
setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email
label_between: between
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
......@@ -658,6 +658,7 @@ de:
label_roadmap_overdue: "%{value} verspätet"
label_roadmap_no_issues: Keine Tickets für diese Version
label_search: Suche
search_input_placeholder: Suche ...
label_result_plural: Resultate
label_all_words: Alle Wörter
label_wiki: Wiki
......@@ -1001,3 +1002,15 @@ de:
description_date_from: Startdatum eintragen
description_date_to: Enddatum eintragen
field_custom_filter: Custom LDAP filter
label_toc: "Inhaltsverzeichnis"
text_display_subprojects: Display subprojects
text_current_project: Current project
setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email
label_mail_handler_confirmation: "Confirmation of email submission: %{subject}"
label_mail_handler_errors_with_submission: "There were errors with your email submission:"
label_document_watchers: Watchers
setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email
label_between: between
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
......@@ -984,3 +984,16 @@ el:
description_date_from: Enter start date
label_deleted_custom_field: (deleted custom field)
field_custom_filter: Custom LDAP filter
text_display_subprojects: Display subprojects
text_current_project: Current project
label_toc: Contents
search_input_placeholder: search ...
setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email
label_mail_handler_confirmation: "Confirmation of email submission: %{subject}"
label_mail_handler_errors_with_submission: "There were errors with your email submission:"
label_document_watchers: Watchers
setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email
label_between: between
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
......@@ -988,3 +988,16 @@ en-GB:
description_date_from: Enter start date
label_deleted_custom_field: (deleted custom field)
field_custom_filter: Custom LDAP filter
text_display_subprojects: Display subprojects
text_current_project: Current project
label_toc: Contents
search_input_placeholder: search ...
setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email
label_mail_handler_confirmation: "Confirmation of email submission: %{subject}"
label_mail_handler_errors_with_submission: "There were errors with your email submission:"
label_document_watchers: Watchers
setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email
label_between: between
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
......@@ -157,6 +157,7 @@ en:
notice_file_not_found: The page you were trying to access doesn't exist or has been removed.
notice_locking_conflict: Data has been updated by another user.
notice_not_authorized: You are not authorized to access this page.
notice_not_authorized_action: You are not authorized to perform this action.
notice_not_authorized_archived_project: The project you're trying to access has been archived.
notice_email_sent: "An email was sent to %{value}"
notice_email_error: "An error occurred while sending mail (%{value})"
......@@ -368,6 +369,8 @@ en:
setting_commit_logtime_activity_id: Activity for logged time
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
setting_issue_startdate_is_adddate: Use current date as start date for new issues
setting_mail_handler_confirmation_on_success: "Send confirmation email on successful incoming email"
setting_mail_handler_confirmation_on_failure: "Send confirmation email on failed incoming email"
permission_add_project: Create project
permission_add_subprojects: Create subprojects
......@@ -610,6 +613,7 @@ en:
label_in_more_than: in more than
label_greater_or_equal: '>='
label_less_or_equal: '<='
label_between: "between"
label_in: in
label_today: today
label_all_time: all time
......@@ -657,6 +661,7 @@ en:
label_roadmap_overdue: "%{value} late"
label_roadmap_no_issues: No issues for this version
label_search: Search
search_input_placeholder: search ...
label_result_plural: Results
label_all_words: All words
label_wiki: Wiki
......@@ -772,6 +777,7 @@ en:
label_incoming_emails: Incoming emails
label_generate_key: Generate a key
label_issue_watchers: Watchers
label_document_watchers: Watchers
label_example: Example
label_display: Display
label_sort: Sort
......@@ -814,6 +820,10 @@ en:
label_notify_member_plural: Email issue updates
label_path_encoding: Path encoding
label_deleted_custom_field: '(deleted custom field)'
label_toc: "Contents"
label_mail_handler_confirmation: "Confirmation of email submission: %{subject}"
label_mail_handler_failure: "Failed email submission: %{subject}"
label_mail_handler_errors_with_submission: "There were errors with your email submission:"
button_login: Login
button_submit: Submit
......@@ -937,6 +947,9 @@ en:
text_default_encoding: "Default: UTF-8"
text_mercurial_repo_example: "local repository (e.g. /hgrepo, c:\\hgrepo)"
text_git_repo_example: "a bare and local repository (e.g. /gitrepo, c:\\gitrepo)"
text_display_subprojects: Display subprojects
text_current_project: Current project
text_mail_handler_confirmation_successful: "Your email has been successful added at the following url"
default_role_manager: Manager
default_role_developer: Developer
......
......@@ -1021,3 +1021,16 @@ es:
description_date_from: Enter start date
label_deleted_custom_field: (deleted custom field)
field_custom_filter: Custom LDAP filter
text_display_subprojects: Display subprojects
text_current_project: Current project
label_toc: Contents
search_input_placeholder: search ...
setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email
label_mail_handler_confirmation: "Confirmation of email submission: %{subject}"
label_mail_handler_errors_with_submission: "There were errors with your email submission:"
label_document_watchers: Watchers
setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email
label_between: between
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment