Compare commits

..

34 Commits

Author SHA1 Message Date
Felix Schäfer 4424841784 Bump version to v2.9.0 2013-01-29 23:13:50 +01:00
Felix Schäfer 2b8ec7c80f Update Changelog for v2.9.0 2013-01-29 23:13:16 +01:00
Felix Schäfer 066f616210 Remove Rails patches which are already included in Rails 2.3.16 #1219 2013-01-29 23:11:27 +01:00
Felix Schäfer 8ea58b6fd5 Bump Rails version to 2.3.16 #1219 2013-01-29 23:11:07 +01:00
Holger Just bfdc43ba73 Bump version to v2.8.1 2013-01-16 23:29:13 +01:00
Holger Just 2b909243ee Update Changelog for v2.8.1 2013-01-16 23:28:51 +01:00
Holger Just b7a82ac691 Fix for CVE-2013-0155 in Rails 2013-01-16 23:27:30 +01:00
Holger Just 964d19cc57 Bump version to 2.8.0 2013-01-09 14:11:46 +01:00
Holger Just dd945c78c3 Update Changelog for v2.8.0 2013-01-09 14:11:18 +01:00
Holger Just 41e349888b Remove Rails patches which are already included in Rails 2.3.15 #1200 2013-01-09 14:11:18 +01:00
Holger Just dca36c222a Bump Rails version to 2.3.15 #1200 2013-01-09 14:11:18 +01:00
Holger Just 524ef942d9 Bump version to v2.7.4 2013-01-06 23:56:43 +01:00
Holger Just e06dd303db Update Changelog for v2.7.4 2013-01-06 23:56:23 +01:00
Holger Just e2bc4e905a Update Copyright for 2013
We programmers have a nice new years tradition: We revisit all of
our projects and add 1 to a small number near a "(c)".

-- Volker Dusch
https://twitter.com/__edorian/status/153801913442373633
2013-01-06 23:52:16 +01:00
Holger Just 6ece1687de Fix XSS vulnerabilities in Rails (CVE-2012-3464, CVE-2012-3465) #1113 #1114 2013-01-06 23:50:49 +01:00
Holger Just 6d87b8b297 SQL Injection Vulnerability in Ruby on Rails (CVE-2012-5664) #1195 2013-01-06 23:50:32 +01:00
Holger Just bd509a4008 Bump version to 2.7.3 2012-06-13 10:29:12 +02:00
Holger Just b0ec4c140d Update changelog for v2.7.3 2012-06-13 10:28:55 +02:00
Holger Just e178f1ce9c Fix SQL injection via nested hashes in conditions. CVE-2012-2695 #1037 2012-06-13 10:27:30 +02:00
Holger Just c3d3bec47f Fix SQL injection via nested hashes in conditions (CVE-2012-2694) #1036 2012-06-13 10:27:21 +02:00
Holger Just 8d56d32774 Bump to 2.7.2 2012-06-09 18:17:46 +02:00
Holger Just 4456440535 Update changelog for v2.7.2 2012-06-09 18:17:14 +02:00
Holger Just f959b9bdb9 [#1025] Fix Rails vulnerability (CVE-2012-2660) 2012-06-09 18:03:41 +02:00
Holger Just 9d32e68ec0 Bump version to 2.7.1 2012-04-04 14:09:08 +02:00
Jean-Philippe Lang 80289c5a70 Set user_id as a protected attribute (#922). 2012-04-04 14:06:01 +02:00
Jean-Philippe Lang 902c624b47 Prevent mass-assignment vulnerability when adding/updating a wiki (#922). 2012-04-04 14:06:00 +02:00
Jean-Philippe Lang aee7d7315b Prevent mass-assignment vulnerability when adding/updating a version (#922). 2012-04-04 14:05:41 +02:00
Jean-Philippe Lang 1f10817444 Prevent mass-assignment vulnerability when adding/updating a time entry (#922). 2012-04-04 13:39:37 +02:00
Jean-Philippe Lang ea3ff66b8e Use safe_attributes= just like in #create. (#922) 2012-04-04 13:39:37 +02:00
Jean-Philippe Lang ee99b2de03 Prevent mass-assignment vulnerability when adding/updating a news (#922). 2012-04-04 13:39:37 +02:00
Jean-Philippe Lang 4c322d379e Prevent mass-assignment vulnerability when adding/updating a forum message (#922). 2012-04-04 13:39:36 +02:00
Jean-Philippe Lang f12b9fca08 Prevent mass-assignment vulnerability when adding a project member (#922). 2012-04-04 13:39:36 +02:00
Jean-Philippe Lang 296b3173ef Prevent mass-assignment vulnerability when adding/updating an issue category (#922). 2012-04-04 13:39:20 +02:00
Jean-Philippe Lang c651ba1a98 Prevent mass-assignment vulnerability when adding/updating a document (#922).
Conflicts:

	app/controllers/documents_controller.rb
2012-04-04 13:30:21 +02:00
867 changed files with 12505 additions and 15077 deletions

4
.gitignore vendored
View File

@ -4,7 +4,6 @@
/config/configuration.yml
/config/database.yml
/config/email.yml
/config/setup_load_paths.rb
/config/initializers/session_store.rb
/coverage
/db/*.db
@ -29,6 +28,3 @@ doc/app
/Gemfile.lock
/Gemfile.local
/.rvmrc*
/*.iml
/.idea
.rbx

View File

@ -1,59 +0,0 @@
language: ruby
rvm:
- 1.8.7
- 1.9.2
- 1.9.3
- rbx-18mode
env:
- "TEST_SUITE=units RAILS_ENV=test DB=mysql BUNDLE_WITHOUT=rmagick:mysql2:postgres:sqlite"
- "TEST_SUITE=units RAILS_ENV=test DB=mysql2 BUNDLE_WITHOUT=rmagick:mysql:postgres:sqlite"
- "TEST_SUITE=units RAILS_ENV=test DB=postgres BUNDLE_WITHOUT=rmagick:mysql:mysql2:sqlite"
- "TEST_SUITE=units RAILS_ENV=test DB=sqlite BUNDLE_WITHOUT=rmagick:mysql:mysql2:postgres"
- "TEST_SUITE=functionals RAILS_ENV=test DB=mysql BUNDLE_WITHOUT=rmagick:mysql2:postgres:sqlite"
- "TEST_SUITE=functionals RAILS_ENV=test DB=mysql2 BUNDLE_WITHOUT=rmagick:mysql:postgres:sqlite"
- "TEST_SUITE=functionals RAILS_ENV=test DB=postgres BUNDLE_WITHOUT=rmagick:mysql:mysql2:sqlite"
- "TEST_SUITE=functionals RAILS_ENV=test DB=sqlite BUNDLE_WITHOUT=rmagick:mysql:mysql2:postgres"
- "TEST_SUITE=integration RAILS_ENV=test DB=mysql BUNDLE_WITHOUT=rmagick:mysql2:postgres:sqlite"
- "TEST_SUITE=integration RAILS_ENV=test DB=mysql2 BUNDLE_WITHOUT=rmagick:mysql:postgres:sqlite"
- "TEST_SUITE=integration RAILS_ENV=test DB=postgres BUNDLE_WITHOUT=rmagick:mysql:mysql2:sqlite"
- "TEST_SUITE=integration RAILS_ENV=test DB=sqlite BUNDLE_WITHOUT=rmagick:mysql:mysql2:postgres"
matrix:
exclude:
- rvm: 1.9.2
env: "TEST_SUITE=units RAILS_ENV=test DB=mysql BUNDLE_WITHOUT=rmagick:mysql2:postgres:sqlite"
- rvm: 1.9.2
env: "TEST_SUITE=functionals RAILS_ENV=test DB=mysql BUNDLE_WITHOUT=rmagick:mysql2:postgres:sqlite"
- rvm: 1.9.2
env: "TEST_SUITE=integration RAILS_ENV=test DB=mysql BUNDLE_WITHOUT=rmagick:mysql2:postgres:sqlite"
- rvm: 1.9.3
env: "TEST_SUITE=units RAILS_ENV=test DB=mysql BUNDLE_WITHOUT=rmagick:mysql2:postgres:sqlite"
- rvm: 1.9.3
env: "TEST_SUITE=functionals RAILS_ENV=test DB=mysql BUNDLE_WITHOUT=rmagick:mysql2:postgres:sqlite"
- rvm: 1.9.3
env: "TEST_SUITE=integration RAILS_ENV=test DB=mysql BUNDLE_WITHOUT=rmagick:mysql2:postgres:sqlite"
- rvm: rbx-18mode
env: "TEST_SUITE=units RAILS_ENV=test DB=mysql BUNDLE_WITHOUT=rmagick:mysql2:postgres:sqlite"
- rvm: rbx-18mode
env: "TEST_SUITE=functionals RAILS_ENV=test DB=mysql BUNDLE_WITHOUT=rmagick:mysql2:postgres:sqlite"
- rvm: rbx-18mode
env: "TEST_SUITE=integration RAILS_ENV=test DB=mysql BUNDLE_WITHOUT=rmagick:mysql2:postgres:sqlite"
allow_failures:
- rvm: rbx-18mode
before_install:
- "sudo apt-get update -qq"
- "sudo apt-get --no-install-recommends install bzr cvs git mercurial subversion"
before_script:
- "rvm rubygems 1.8.25" # Rubygems 2.0.x fails with Rails 2.3
- "rake ci:travis:prepare"
- "rm -rf tmp/test/darcs_repository" # Don't test Darcs on Travis. It breaks there :(
script: "bundle exec rake test:$TEST_SUITE"
branches:
only:
- unstable
- master
- stable
- /^stable-.*$/
- /^release-.*$/
notifications:
email: false
irc: "irc.freenode.org#chiliproject"

View File

@ -1,33 +0,0 @@
For the impatient: [report][cpo_new-issue], confirm, claim,
[fork][gh_chiliproject], [branch][cpo_contribute-code-branch],
[write][cpo_code-standards], [test][cpo_code-review], push.
The short version:
* Make sure the issue you are working on is reported and confirmed, add a note
to the issue to claim your intention to work on it.
* Fork [ChiliProject on GitHub][gh_chiliproject]
* Create a new branch from `master` with a descriptive name prefixed by the
issue ID (Example: `123-change_background_from_black_to_blue`).
* Make changes according to our [Code Standards][cpo_code-standards].
1. Be sure to include tests as necessary.
1. Make sure to not break existing tests.
1. Please try to make sure your code is going to pass a [Code
Review][cpo_code-review] prior to submitting the patch. If in doubt, just ask.
* Either upload your branch to GitHub and send a pull request to the
ChiliProject repository, or attach a patch to the issue on ChiliProject. If
you send a pull request on GitHub, remember to link to the pull request in the
issue you create on ChiliProject and to link to the issue on ChiliProject in
the pull request on GitHub.
* Make sure you watch the corresponding issue in case any discussion arises or
improvements are needed.
The long version is on the [Contribute Code][cpo_contribute-code] page.
[cpo_new-issue]: https://www.chiliproject.org/projects/chiliproject/issues/new
[cpo_contribute-code-branch]: https://www.chiliproject.org/projects/chiliproject/wiki/Contribute_Code#Branch
[cpo_contribute-code]: https://www.chiliproject.org/projects/chiliproject/wiki/Contribute_Code
[cpo_code-standards]: https://www.chiliproject.org/projects/chiliproject/wiki/Code_Standards
[cpo_code-review]: https://www.chiliproject.org/projects/chiliproject/wiki/Code_Review
[gh_chiliproject]: https://github.com/chiliproject/chiliproject

27
Gemfile
View File

@ -1,32 +1,23 @@
# -*- coding: utf-8 -*-
source "https://rubygems.org"
source :rubygems
gem "rails", "2.3.18"
gem "rails", "2.3.16"
gem "json", "~> 1.7.7"
gem "coderay", "~> 1.0.0"
gem "coderay", "~> 0.9.7"
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"
gem 'gravatarify', '~> 3.0.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]
gem "tzinfo", "~> 0.3.31" # Fixes #903. Not required for Rails >= 3.2
group :test do
gem 'shoulda', '~> 2.10.3'
# Shoulda doesn't work nice on 1.9.3 and seems to need test-unit explicitely…
gem 'test-unit', :platforms => [:mri_19]
gem 'edavis10-object_daddy', :require => 'object_daddy'
gem 'mocha', '0.12.1'
# capybara 2 drops ruby 1.8.7 compatibility
gem 'capybara', '< 2.0.0'
gem 'mocha'
gem 'capybara'
end
group :ldap do
gem "net-ldap", '~> 0.3.1'
gem "net-ldap", '~> 0.2.2'
end
group :openid do
@ -57,13 +48,13 @@ end
# orders of magnitude compared to their native counterparts. You have been
# warned.
platforms :mri, :mingw, :rbx do
platforms :mri, :mingw do
group :mysql2 do
gem "mysql2", "~> 0.2.7"
end
group :postgres do
gem "pg"
gem "pg", "~> 0.9.0"
# gem "postgres-pr"
end
end
@ -79,7 +70,7 @@ platforms :mri_18, :mingw_18 do
end
end
platforms :mri_19, :mingw_19, :rbx do
platforms :mri_19, :mingw_19 do
group :sqlite do
gem "sqlite3"
end

View File

@ -1,4 +1,4 @@
= ChiliProject {<img src="https://travis-ci.org/chiliproject/chiliproject.png?branch=master" />}[http://travis-ci.org/chiliproject/chiliproject]
= ChiliProject
ChiliProject is a web based project management system. It supports your team throughout the complete project life cycle, from setting up and discussing a project plan, over tracking issues and reporting work progress to collaboratively sharing knowledge.

View File

@ -8,5 +8,3 @@ require 'rake/testtask'
require 'rdoc/task'
require 'tasks/rails'
# Load rake tasks from plugins in chiliproject_plugins
Dir["#{RAILS_ROOT}/vendor/chiliproject_plugins/*/lib/tasks/**/*.rake"].sort.each { |ext| load ext }

View File

@ -37,7 +37,7 @@ class AccountController < ApplicationController
def lost_password
redirect_to(home_url) && return unless Setting.lost_password?
if params[:token]
@token = Token.find_by_action_and_value("recovery", params[:token].to_s)
@token = Token.find_by_action_and_value("recovery", params[:token])
redirect_to(home_url) && return unless @token and !@token.expired?
@user = @token.user
if request.post?
@ -53,7 +53,7 @@ class AccountController < ApplicationController
return
else
if request.post?
user = User.find_by_mail(params[:mail].to_s)
user = User.find_by_mail(params[:mail])
# user not found in db
(flash.now[:error] = l(:notice_account_unknown_email); return) unless user
# user uses an external authentification
@ -109,7 +109,7 @@ class AccountController < ApplicationController
# Token based account activation
def activate
redirect_to(home_url) && return unless Setting.self_registration? && params[:token]
token = Token.find_by_action_and_value('register', params[:token].to_s)
token = Token.find_by_action_and_value('register', params[:token])
redirect_to(home_url) && return unless token and !token.expired?
user = token.user
redirect_to(home_url) && return unless user.registered?

View File

@ -33,7 +33,7 @@ class ActivitiesController < ApplicationController
:with_subprojects => @with_subprojects,
:author => @author)
@activity.scope_select {|t| !params["show_#{t}"].nil?}
@activity.scope = (@author.nil? ? :default : :all) if @activity.scope.empty? unless params[:set_filter]
@activity.scope = (@author.nil? ? :default : :all) if @activity.scope.empty?
events = @activity.events(@date_from, @date_to)

View File

@ -19,10 +19,6 @@ 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
@ -77,7 +73,7 @@ class AdminController < ApplicationController
def info
@db_adapter_name = ActiveRecord::Base.connection.adapter_name
@checklist = [
[:text_default_administrator_account_changed, !User.find_by_login("admin").try(:check_password?, "admin")],
[:text_default_administrator_account_changed, User.find(:first, :conditions => ["login=? and hashed_password=?", 'admin', User.hash_password('admin')]).nil?],
[:text_file_repository_writable, File.writable?(Attachment.storage_path)],
[:text_plugin_assets_writable, File.writable?(Engines.public_directory)],
[:text_rmagick_available, Object.const_defined?(:Magick)]

View File

@ -31,6 +31,18 @@ class ApplicationController < ActionController::Base
cookies.delete(:autologin)
end
# Remove broken cookie after upgrade from 0.8.x (#4292)
# See https://rails.lighthouseapp.com/projects/8994/tickets/3360
# TODO: remove it when Rails is fixed
before_filter :delete_broken_cookies
def delete_broken_cookies
if cookies['_chiliproject_session'] && cookies['_chiliproject_session'] !~ /--/
cookies.delete '_chiliproject_session'
redirect_to home_path
return false
end
end
# FIXME: Remove this when all of Rack and Rails have learned how to
# properly use encodings
before_filter :params_filter
@ -52,16 +64,7 @@ class ApplicationController < ActionController::Base
before_filter :user_setup, :check_if_login_required, :set_localization
filter_parameter_logging :password
# 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{
# manually apply basic before_filters which aren't applied by default here
user_setup
check_if_login_required
set_localization
render_404
}
rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token
include Redmine::Search::Controller
include Redmine::MenuManager::MenuController
@ -72,6 +75,8 @@ 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
@ -89,11 +94,11 @@ class ApplicationController < ActionController::Base
user
elsif params[:format] == 'atom' && params[:key] && accept_key_auth_actions.include?(params[:action])
# RSS key authentication does not start a session
User.find_by_rss_key(params[:key].to_s)
User.find_by_rss_key(params[:key])
elsif Setting.rest_api_enabled? && api_request?
if (key = api_key_from_request) && accept_key_auth_actions.include?(params[:action])
# Use API key
User.find_by_api_key(key.to_s)
User.find_by_api_key(key)
else
# HTTP Basic, either username/password or API key/random
authenticate_with_http_basic do |username, password|
@ -330,6 +335,13 @@ class ApplicationController < ActionController::Base
request.xhr? ? false : 'base'
end
def invalid_authenticity_token
if api_request?
logger.error "Form authenticity token is missing or is invalid. API calls must include a proper Content-type header (text/xml or text/json)."
end
render_error "Invalid form authenticity token."
end
def render_feed(items, options={})
@items = items || []
@items.sort! {|x,y| y.event_datetime <=> x.event_datetime }

View File

@ -13,8 +13,7 @@
#++
class AutoCompletesController < ApplicationController
before_filter :find_project, :only => :issues
before_filter :require_admin, :only => :projects
before_filter :find_project
def issues
@issues = []
@ -34,38 +33,6 @@ 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

View File

@ -21,10 +21,7 @@ class CommentsController < ApplicationController
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
def create
raise Unauthorized unless @news.commentable?
@comment = Comment.new
@comment.safe_attributes = params[:comment]
@comment = Comment.new(params[:comment])
@comment.author = User.current
if @news.comments << @comment
flash[:notice] = l(:label_comment_added)

View File

@ -45,32 +45,19 @@ class DocumentsController < ApplicationController
def new
@document = @project.documents.build
@document.safe_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.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
if request.post? && @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
def edit
@categories = DocumentCategory.all
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
if request.post? and @document.update_attributes(params[:document])
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'show', :id => @document
end
end
@ -83,12 +70,7 @@ class DocumentsController < ApplicationController
attachments = Attachment.attach_files(@document, params[:attachments])
render_attachment_warning_if_needed(@document)
if attachments.present? && attachments[:files].present? && Setting.notified_events.include?('document_added')
# TODO: refactor
@document.recipients.each do |recipient|
Mailer.deliver_attachments_added(attachments[:files], recipient)
end
end
Mailer.deliver_attachments_added(attachments[:files]) if attachments.present? && attachments[:files].present? && Setting.notified_events.include?('document_added')
redirect_to :action => 'show', :id => @document
end

View File

@ -42,11 +42,7 @@ class FilesController < ApplicationController
render_attachment_warning_if_needed(container)
if !attachments.empty? && !attachments[:files].blank? && Setting.notified_events.include?('file_added')
# 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
Mailer.deliver_attachments_added(attachments[:files])
end
redirect_to project_files_path(@project)
end

View File

@ -126,19 +126,16 @@ 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
@membership = Member.edit_membership(params[:membership_id], params[:membership], @group)
@membership.save if request.post?
respond_to do |format|
if @membership.valid?
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' }

View File

@ -12,8 +12,6 @@
# 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]
@ -86,22 +84,6 @@ 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
@ -118,9 +100,4 @@ 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

View File

@ -14,7 +14,6 @@
class LdapAuthSourcesController < AuthSourcesController
menu_item :ldap_authentication, :only => [:index]
protected
def auth_source_class

View File

@ -107,7 +107,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 << "$('message_subject').value = \"#{subject}\";"
page << "$('reply_subject').value = \"#{subject}\";"
page.<< "$('message_content').value = \"#{content}\";"
page.show 'reply'
page << "Form.Element.focus('message_content');"

View File

@ -20,7 +20,6 @@ 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?
@ -43,7 +42,6 @@ class QueriesController < ApplicationController
@query.add_filters(params[:fields] || params[:f], params[:operators] || params[:op], params[:values] || params[:v]) if params[:fields] || params[:f]
@query.attributes = params[:query]
@query.project = nil if params[:query_is_for_all]
@query.display_subprojects = params[:display_subprojects] if params[:display_subprojects].present?
@query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
@query.group_by ||= params[:group_by]
@query.column_names = params[:c] if params[:c]

View File

@ -72,7 +72,8 @@ class TimeEntryReportsController < ApplicationController
@periods = []
# Date#at_beginning_of_ not supported in Rails 1.2.x
date_from = @from.to_time
while date_from <= @to.to_time
# 100 columns max
while date_from <= @to.to_time && @periods.length < 100
case @columns
when 'year'
@periods << "#{date_from.year}"
@ -160,9 +161,6 @@ 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},

View File

@ -197,16 +197,8 @@ 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
@membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
@membership.save if request.post?
respond_to do |format|
if @membership.valid?
format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }

View File

@ -16,7 +16,6 @@ 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 ],
@ -35,12 +34,9 @@ class WatchersController < ApplicationController
end
def new
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?
@watcher = Watcher.new(params[:watcher])
@watcher.watchable = @watched
@watcher.save if request.post?
respond_to do |format|
format.html { redirect_to :back }
format.js do
@ -54,7 +50,7 @@ class WatchersController < ApplicationController
end
def destroy
@watched.set_watcher(Principal.find(params[:user_id]), false) if request.post?
@watched.set_watcher(User.find(params[:user_id]), false) if request.post?
respond_to do |format|
format.html { redirect_to :back }
format.js do
@ -98,24 +94,4 @@ 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

View File

@ -1,37 +0,0 @@
#-- encoding: UTF-8
#-- copyright
# ChiliProject is a project management system.
#
# Copyright (C) 2010-2013 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

View File

@ -1,79 +0,0 @@
#-- encoding: UTF-8
#-- copyright
# ChiliProject is a project management system.
#
# Copyright (C) 2010-2013 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

View File

@ -1,17 +0,0 @@
#-- encoding: UTF-8
#-- copyright
# ChiliProject is a project management system.
#
# Copyright (C) 2010-2013 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

View File

@ -1,17 +0,0 @@
#-- encoding: UTF-8
#-- copyright
# ChiliProject is a project management system.
#
# Copyright (C) 2010-2013 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

View File

@ -1,17 +0,0 @@
#-- encoding: UTF-8
#-- copyright
# ChiliProject is a project management system.
#
# Copyright (C) 2010-2013 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

View File

@ -1,17 +0,0 @@
#-- encoding: UTF-8
#-- copyright
# ChiliProject is a project management system.
#
# Copyright (C) 2010-2013 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

View File

@ -1,17 +0,0 @@
#-- encoding: UTF-8
#-- copyright
# ChiliProject is a project management system.
#
# Copyright (C) 2010-2013 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

View File

@ -16,8 +16,9 @@ require 'forwardable'
require 'cgi'
module ApplicationHelper
include Redmine::WikiFormatting::Macros::Definitions
include Redmine::I18n
include Gravatarify::Helper
include GravatarHelper::PublicMethods
extend Forwardable
def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
@ -223,15 +224,17 @@ module ApplicationHelper
end
# Renders the project quick-jump box
def render_project_jump_box(projects = [], html_options = {})
projects ||= User.current.memberships.collect(&:project).compact.uniq
def render_project_jump_box
projects = User.current.memberships.collect(&:project).compact.uniq
if projects.any?
# 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
select_tag "", option_tags, html_options.merge({ :onchange => "if (this.value != \'\') { window.location = this.value; }" })
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|
{ :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
end
s << '</select>'
s
end
end
@ -285,15 +288,7 @@ module ApplicationHelper
def principals_check_box_tags(name, principals)
s = ''
principals.sort.each do |principal|
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"
s << "<label>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
end
s
end
@ -394,9 +389,7 @@ module ApplicationHelper
end
def page_header_title
if @page_header_title.present?
h(@page_header_title)
elsif @project.nil? || @project.new_record?
if @project.nil? || @project.new_record?
h(Setting.app_title)
else
b = []
@ -436,9 +429,8 @@ module ApplicationHelper
css << 'theme-' + theme.name
end
css << 'project-' + @project.id.to_s if @project.present?
css << 'controller-' + params[:controller] if params[:controller]
css << 'action-' + params[:action] if params[:action]
css << 'controller-' + params[:controller]
css << 'action-' + params[:action]
css.join(' ')
end
@ -455,55 +447,23 @@ module ApplicationHelper
case args.size
when 1
obj = options[:object]
input_text = args.shift
text = args.shift
when 2
obj = args.shift
attr = args.shift
input_text = obj.send(attr).to_s
text = obj.send(attr).to_s
else
raise ArgumentError, 'invalid arguments to textilizable'
end
return '' if input_text.blank?
return '' if text.blank?
project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
only_path = options.delete(:only_path) == false ? false : true
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
text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr) { |macro, args| exec_macro(macro, obj, args) }
@parsed_headings = []
text = parse_non_pre_blocks(text) do |text|
[:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_headings, :parse_relative_urls].each do |method_name|
[:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_headings].each do |method_name|
send method_name, text, project, obj, attr, only_path, options
end
end
@ -544,41 +504,6 @@ module ApplicationHelper
parsed
end
RELATIVE_LINK_RE = %r{
<a
(?:
(\shref=
(?: # the href and link
(?:'(\/[^>]+?)')|
(?:"(\/[^>]+?)")
)
)|
[^>]
)*
>
[^<]*?<\/a> # content and closing link tag.
}x unless const_defined?(:RELATIVE_LINK_RE)
def parse_relative_urls(text, project, obj, attr, only_path, options)
return if only_path
text.gsub!(RELATIVE_LINK_RE) do |m|
href, relative_url = $1, $2 || $3
next m unless href.present?
if defined?(request) && request.present?
# we have a request!
protocol, host_with_port = request.protocol, request.host_with_port
elsif @controller
# use the same methods as url_for in the Mailer
url_opts = @controller.class.default_url_options
next m unless url_opts && url_opts[:protocol] && url_opts[:host]
protocol, host_with_port = "#{url_opts[:protocol]}://", url_opts[:host]
else
next m
end
m.sub href, " href=\"#{protocol}#{host_with_port}#{relative_url}\""
end
end
def parse_inline_attachments(text, project, obj, attr, only_path, options)
# when using an image link, try to use an attachment, if possible
if options[:attachments] || (obj && obj.respond_to?(:attachments))
@ -787,7 +712,7 @@ module ApplicationHelper
end
end
TOC_RE = /<p>\{%\s*toc(_right|_left)?\s*%\}<\/p>/i unless const_defined?(:TOC_RE)
TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
# Renders the TOC with given headings
def replace_toc(text, headings)
@ -795,14 +720,10 @@ module ApplicationHelper
if headings.empty?
''
else
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>"
div_class = 'toc'
div_class << ' right' if $1 == '>'
div_class << ' left' if $1 == '<'
out = "<ul class=\"#{div_class}\"><li>"
root = headings.map(&:first).min
current = root
started = false
@ -820,7 +741,6 @@ module ApplicationHelper
end
out << '</li></ul>' * (current - root)
out << '</li></ul>'
out << '</div></fieldset>'
end
end
end
@ -852,7 +772,7 @@ module ApplicationHelper
def back_url_hidden_field_tag
back_url = params[:back_url] || request.env['HTTP_REFERER']
back_url = CGI.unescape(back_url.to_s)
hidden_field_tag('back_url', CGI.escape(back_url), :id => nil) unless back_url.blank?
hidden_field_tag('back_url', CGI.escape(back_url)) unless back_url.blank?
end
def check_all_links(form_name)
@ -868,10 +788,12 @@ module ApplicationHelper
pcts << (100 - pcts[1] - pcts[0])
width = options[:width] || '100px;'
legend = options[:legend] || ''
content_tag('div',
content_tag('div', '', :style => "width: #{pcts[0]}%;", :class => 'closed ui-progressbar-value ui-widget-header ui-corner-left') +
content_tag('div', '', :style => "width: #{pcts[1]}%;", :class => 'done ui-progressbar-value ui-widget-header'),
:class => 'progress ui-progressbar ui-widget ui-widget-content ui-corner-all', :style => "width: #{width};") +
content_tag('table',
content_tag('tr',
(pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : '') +
(pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : '') +
(pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : '')
), :class => 'progress', :style => "width: #{width};") +
content_tag('p', legend, :class => 'pourcent')
end
@ -884,7 +806,7 @@ module ApplicationHelper
def context_menu(url)
unless @context_menu_included
content_for :header_tags do
javascript_include_tag('context_menu.jquery') +
javascript_include_tag('context_menu') +
stylesheet_link_tag('context_menu')
end
if l(:direction) == 'rtl'
@ -894,7 +816,7 @@ module ApplicationHelper
end
@context_menu_included = true
end
javascript_tag "jQuery(document).ContextMenu('#{ url_for(url) }')"
javascript_tag "new ContextMenu('#{ url_for(url) }')"
end
def context_menu_link(name, url, options={})
@ -914,25 +836,33 @@ module ApplicationHelper
end
def calendar_for(field_id)
javascript_tag("jQuery('##{field_id}').datepicker(datepickerSettings)")
include_calendar_headers_tags
image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
end
def jquery_datepicker_settings
start_of_week = Setting.start_of_week.to_s
start_of_week_string = start_of_week.present? ? "firstDay: '#{start_of_week}', " : ''
script = javascript_tag("var datepickerSettings = {" +
start_of_week_string +
"showOn: 'both', " +
"buttonImage: '" + path_to_image('/images/calendar.png') + "', " +
"buttonImageOnly: true, " +
"showButtonPanel: true, " +
"dateFormat: 'yy-mm-dd' " +
"}")
unless current_language == :en
jquery_locale = l("jquery.ui", :default => current_language.to_s)
script << javascript_include_tag("libs/ui/i18n/jquery.ui.datepicker-#{jquery_locale}.js")
def include_calendar_headers_tags
unless @calendar_headers_tags_included
@calendar_headers_tags_included = true
content_for :header_tags do
start_of_week = case Setting.start_of_week.to_i
when 1
'Calendar._FD = 1;' # Monday
when 7
'Calendar._FD = 0;' # Sunday
when 6
'Calendar._FD = 6;' # Saturday
else
'' # use language
end
javascript_include_tag('calendar/calendar') +
javascript_include_tag("calendar/lang/calendar-#{current_language.to_s.downcase}.js") +
javascript_tag(start_of_week) +
javascript_include_tag('calendar/calendar-setup') +
stylesheet_link_tag('calendar')
end
end
script
end
def content_for(name, content = nil, &block)
@ -945,29 +875,10 @@ module ApplicationHelper
(@has_content && @has_content[name]) || false
end
# Returns the gravatar image tag for the given email
# +email+ is a string with an email address
def gravatar(email, options={})
gravatarify_options = {}
gravatarify_options[:secure] = options.delete :ssl
[:default, :size, :rating, :filetype].each {|key| gravatarify_options[key] = options.delete key}
# Default size is 50x50 px
gravatarify_options[:size] ||= 50
options[:class] ||= 'gravatar'
gravatarify_options[:html] = options
gravatar_tag email, gravatarify_options
end
# Returns the avatar image tag for the given +user+ if avatars are enabled
# +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)
@ -987,7 +898,6 @@ module ApplicationHelper
unless User.current.pref.warn_on_leaving_unsaved == '0'
tags << "\n" + javascript_tag("Event.observe(window, 'load', function(){ new WarnLeavingUnsaved('#{escape_javascript( l(:text_warn_on_leaving_unsaved) )}'); });")
end
tags << jquery_datepicker_settings
tags
end
@ -1025,72 +935,6 @@ 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
@ -1102,20 +946,4 @@ 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

View File

@ -36,7 +36,8 @@ module CustomFieldsHelper
field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
case field_format.try(:edit_as)
when "date"
date_field_tag(field_name, custom_value.value, :id => field_id, :size => 10)
text_field_tag(field_name, custom_value.value, :id => field_id, :size => 10) +
calendar_for(field_id)
when "text"
text_area_tag(field_name, custom_value.value, :id => field_id, :rows => 3, :style => 'width:90%')
when "bool"
@ -70,7 +71,8 @@ module CustomFieldsHelper
field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
case field_format.try(:edit_as)
when "date"
date_field_tag(field_name, '', :id => field_id, :size => 10)
text_field_tag(field_name, '', :id => field_id, :size => 10) +
calendar_for(field_id)
when "text"
text_area_tag(field_name, '', :id => field_id, :rows => 3, :style => 'width:90%')
when "bool"

View File

@ -52,14 +52,13 @@ 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('h2', link_to_issue(ancestor))
s << '<div>' + content_tag('p', link_to_issue(ancestor))
end
s << '<div class="subject">' + content_tag('h2', h(issue.subject))
s << '<div>' + content_tag('h3', h(issue.subject))
s << '</div>' * (ancestors.size + 1)
s
end
@ -79,22 +78,6 @@ module IssuesHelper
s
end
def render_parents_and_subtree(issue)
return if issue.leaf? && !issue.parent
s = '<form><table id="issue_tree" class="list">'
issue_list(issue.self_and_ancestors.sort_by(&:lft) + issue.descendants.sort_by(&:lft)) do |el, level|
s << content_tag('tr',
content_tag('td', check_box_tag("ids[]", el.id, false, :id => nil), :class => 'checkbox') +
content_tag('td', link_to_issue(el, :truncate => 60), :class => 'subject') +
content_tag('td', h(el.status)) +
content_tag('td', link_to_user(el.assigned_to)) +
content_tag('td', progress_bar(el.done_ratio, :width => '80px')),
:class => "issue issue-#{el.id} #{"self" if el == issue} hascontextmenu #{level > 0 ? "idnt idnt-#{level}" : nil}")
end
s << '</table></form>'
s
end
def render_custom_fields_rows(issue)
return if issue.custom_field_values.empty?
ordered_values = []

View File

@ -27,34 +27,24 @@ module JournalsHelper
def render_journal(model, journal, options = {})
return "" if journal.initial?
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)
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 }
end
# This renders a journal entry with a header and details
def render_journal_details(journal, header_label = :label_updated_time_by, model=nil, options={})
# This renders a journal entry wiht a header and details
def render_journal_details(journal, header_label = :label_updated_time_by)
header = <<-HTML
<h4>
<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}
<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}")}
#{authoring journal.created_at, journal.user, :label => header_label}
</h4>
HTML
header << render_notes(model, journal, options) unless journal.notes.blank?
if journal.details.any?
details = content_tag "ul", :class => "journal-attributes details" do
details = content_tag "ul", :class => "details" do
journal.details.collect do |detail|
if d = journal.render_detail(detail)
content_tag("li", d)
@ -63,7 +53,7 @@ module JournalsHelper
end
end
content_tag "div", "#{header}#{details}", :class => "journal-details"
content_tag("div", "#{header}#{details}", :id => "change-#{journal.id}", :class => "journal")
end
def render_notes(model, journal, options={})

View File

@ -84,12 +84,11 @@ 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, :display_subprojects => @query.display_subprojects}
session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
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], :display_subprojects => session[:query][:display_subprojects])
@query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
@query.project = @project
end
end

View File

@ -89,12 +89,12 @@ class Changeset < ActiveRecord::Base
# Attribute reader for committer that encodes the committer string to
# the repository log encoding (e.g. UTF-8)
def committer
self.class.to_utf8(read_attribute(:committer), repository_encoding)
self.class.to_utf8(read_attribute(:committer), repository.repo_log_encoding)
end
def before_create
self.committer = self.class.to_utf8(self.committer, repository_encoding)
self.comments = self.class.normalize_comments(self.comments, repository_encoding)
self.committer = self.class.to_utf8(self.committer, repository.repo_log_encoding)
self.comments = self.class.normalize_comments(self.comments, repository.repo_log_encoding)
self.user = repository.find_committer_user(self.committer)
end

View File

@ -13,11 +13,8 @@
#++
class Comment < ActiveRecord::Base
include Redmine::SafeAttributes
belongs_to :commented, :polymorphic => true, :counter_cache => true
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
validates_presence_of :commented, :author, :comments
safe_attributes 'comments'
end

View File

@ -25,7 +25,6 @@ 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
@ -41,9 +40,7 @@ class Document < ActiveRecord::Base
def after_initialize
if new_record?
# FIXME: on Rails 3 use this instead
# self.category ||= DocumentCategory.default
self.category_id = DocumentCategory.default.id if self.category_id == 0
self.category ||= DocumentCategory.default
end
end
@ -54,10 +51,4 @@ class Document < ActiveRecord::Base
end
@updated_on
end
def recipients
mails = super # from acts_as_event
mails += watcher_recipients
mails.uniq
end
end

View File

@ -14,10 +14,6 @@
class DocumentObserver < ActiveRecord::Observer
def after_create(document)
if Setting.notified_events.include?('document_added')
document.recipients.each do |recipient|
Mailer.deliver_document_added(document, recipient)
end
end
Mailer.deliver_document_added(document) if Setting.notified_events.include?('document_added')
end
end

View File

@ -22,11 +22,6 @@ 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
@ -48,9 +43,4 @@ class Group < Principal
:conditions => ["#{Member.table_name}.user_id = ? AND #{MemberRole.table_name}.inherited_from IN (?)", user.id, member.member_role_ids]).each(&:destroy)
end
end
def self.human_attribute_name(attribute_name)
attribute_name = "name" if attribute_name == "lastname"
super(attribute_name)
end
end

View File

@ -103,10 +103,6 @@ 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
@ -368,7 +364,7 @@ class Issue < ActiveRecord::Base
def attachment_removed(obj)
init_journal(User.current)
create_journal
last_journal.update_attribute(:changes, {"attachments_" + obj.id.to_s => [obj.filename, nil]})
last_journal.update_attribute(:changes, {"attachments_" + obj.id.to_s => [obj.filename, nil]}.to_yaml)
end
# Return true if the issue is closed, otherwise false
@ -707,15 +703,6 @@ class Issue < ActiveRecord::Base
projects
end
# Overrides Redmine::Acts::Journalized::Permissions
#
# The default assumption is that journals have the same permissions
# as the journaled object, issue notes have separate permissions though
def journal_editable_by?(journal, user)
return true if journal.user == user && user.allowed_to?(:edit_own_issue_notes, project)
user.allowed_to? :edit_issue_notes, project
end
private
def update_nested_set_attributes

View File

@ -17,9 +17,7 @@ class IssueObserver < ActiveRecord::Observer
def after_create(issue)
if self.send_notification
(issue.recipients + issue.watcher_recipients).uniq.each do |recipient|
Mailer.deliver_issue_add(issue, recipient)
end
Mailer.deliver_issue_add(issue) if Setting.notified_events.include?('issue_added')
end
clear_notification
end

View File

@ -28,10 +28,6 @@ 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])

View File

@ -76,7 +76,7 @@ class Journal < ActiveRecord::Base
end
def editable_by?(user)
journaled.journal_editable_by?(self, user)
journaled.journal_editable_by?(user)
end
def details

View File

@ -27,15 +27,11 @@ class JournalObserver < ActiveRecord::Observer
if journal.initial?
if Setting.notified_events.include?('wiki_content_added')
(wiki_content.recipients + wiki_page.wiki.watcher_recipients).uniq.each do |recipient|
Mailer.deliver_wiki_content_added(wiki_content, recipient)
end
Mailer.deliver_wiki_content_added(wiki_content)
end
else
if Setting.notified_events.include?('wiki_content_updated')
(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
Mailer.deliver_wiki_content_updated(wiki_content)
end
end
end
@ -47,10 +43,7 @@ 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?)
issue = journal.issue
(issue.recipients + issue.watcher_recipients).uniq.each do |recipient|
Mailer.deliver_issue_edit(journal, recipient)
end
Mailer.deliver_issue_edit(journal)
end
end

View File

@ -69,7 +69,6 @@ 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
@ -103,15 +102,12 @@ 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
@ -145,7 +141,6 @@ 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
@ -167,7 +162,6 @@ 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
@ -196,7 +190,6 @@ 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

View File

@ -30,19 +30,20 @@ class Mailer < ActionMailer::Base
{ :host => h, :protocol => Setting.protocol }
end
# Builds a tmail object used to email a recipient of the added issue.
# Builds a tmail object used to email recipients of the added issue.
#
# Example:
# issue_add(issue, 'user@example.com') => tmail object
# Mailer.deliver_issue_add(issue, 'user@example.com') => sends an email to 'user@example.com'
def issue_add(issue, recipient)
# issue_add(issue) => tmail object
# Mailer.deliver_issue_add(issue) => sends an email to issue recipients
def issue_add(issue)
redmine_headers 'Project' => issue.project.identifier,
'Issue-Id' => issue.id,
'Issue-Author' => issue.author.login,
'Type' => "Issue"
redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
message_id issue
recipients [recipient]
recipients issue.recipients
cc(issue.watcher_recipients - @recipients)
subject "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] (#{issue.status.name}) #{issue.subject}"
body :issue => issue,
:issue_url => url_for(:controller => 'issues', :action => 'show', :id => issue)
@ -52,9 +53,9 @@ class Mailer < ActionMailer::Base
# Builds a tmail object used to email recipients of the edited issue.
#
# Example:
# issue_edit(journal, 'user@example.com') => tmail object
# Mailer.deliver_issue_edit(journal, 'user@example.com') => sends an email to issue recipients
def issue_edit(journal, recipient)
# issue_edit(journal) => tmail object
# Mailer.deliver_issue_edit(journal) => sends an email to issue recipients
def issue_edit(journal)
issue = journal.journaled.reload
redmine_headers 'Project' => issue.project.identifier,
'Issue-Id' => issue.id,
@ -64,7 +65,9 @@ class Mailer < ActionMailer::Base
message_id journal
references issue
@author = journal.user
recipients [recipient]
recipients issue.recipients
# Watchers in cc
cc(issue.watcher_recipients - @recipients)
s = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] "
s << "(#{issue.status.name}) " if journal.details['status_id']
s << issue.subject
@ -90,12 +93,12 @@ class Mailer < ActionMailer::Base
# Builds a tmail object used to email users belonging to the added document's project.
#
# Example:
# document_added(document, 'test@example.com') => tmail object
# Mailer.deliver_document_added(document, 'test@example.com') => sends an email to the document's project recipients
def document_added(document, recipient)
# document_added(document) => tmail object
# Mailer.deliver_document_added(document) => sends an email to the document's project recipients
def document_added(document)
redmine_headers 'Project' => document.project.identifier,
'Type' => "Document"
recipients [recipient]
recipients document.recipients
subject "[#{document.project.name}] #{l(:label_document_new)}: #{document.title}"
body :document => document,
:document_url => url_for(:controller => 'documents', :action => 'show', :id => document)
@ -107,7 +110,7 @@ class Mailer < ActionMailer::Base
# Example:
# attachments_added(attachments) => tmail object
# Mailer.deliver_attachments_added(attachments) => sends an email to the project's recipients
def attachments_added(attachments, recipient)
def attachments_added(attachments)
container = attachments.first.container
added_to = ''
added_to_url = ''
@ -115,14 +118,16 @@ class Mailer < ActionMailer::Base
when 'Project'
added_to_url = url_for(:controller => 'files', :action => 'index', :project_id => container)
added_to = "#{l(:label_project)}: #{container}"
recipients container.project.notified_users.select {|user| user.allowed_to?(:view_files, container.project)}.collect {|u| u.mail}
when 'Version'
added_to_url = url_for(:controller => 'files', :action => 'index', :project_id => container.project)
added_to = "#{l(:label_version)}: #{container.name}"
recipients container.project.notified_users.select {|user| user.allowed_to?(:view_files, container.project)}.collect {|u| u.mail}
when 'Document'
added_to_url = url_for(:controller => 'documents', :action => 'show', :id => container.id)
added_to = "#{l(:label_document)}: #{container.title}"
recipients container.recipients
end
recipients [recipient]
redmine_headers 'Project' => container.project.identifier,
'Type' => "Attachment"
subject "[#{container.project.name}] #{l(:label_attachment_new)}"
@ -137,11 +142,11 @@ class Mailer < ActionMailer::Base
# Example:
# news_added(news) => tmail object
# Mailer.deliver_news_added(news) => sends an email to the news' project recipients
def news_added(news, recipient)
def news_added(news)
redmine_headers 'Project' => news.project.identifier,
'Type' => "News"
message_id news
recipients [recipient]
recipients news.recipients
subject "[#{news.project.name}] #{l(:label_news)}: #{news.title}"
body :news => news,
:news_url => url_for(:controller => 'news', :action => 'show', :id => news)
@ -171,13 +176,14 @@ class Mailer < ActionMailer::Base
# Example:
# message_posted(message) => tmail object
# Mailer.deliver_message_posted(message) => sends an email to the recipients
def message_posted(message, recipient)
def message_posted(message)
redmine_headers 'Project' => message.project.identifier,
'Topic-Id' => (message.parent_id || message.id),
'Type' => "Forum"
message_id message
references message.parent unless message.parent.nil?
recipients [recipient]
recipients(message.recipients)
cc((message.root.watcher_recipients + message.board.watcher_recipients).uniq - @recipients)
subject "[#{message.board.project.name} - #{message.board.name} - msg#{message.root.id}] #{message.subject}"
body :message => message,
:message_url => url_for({ :controller => 'messages', :action => 'show', :board_id => message.board, :id => message.root, :r => message, :anchor => "message-#{message.id}" })
@ -189,12 +195,13 @@ class Mailer < ActionMailer::Base
# Example:
# wiki_content_added(wiki_content) => tmail object
# Mailer.deliver_wiki_content_added(wiki_content) => sends an email to the project's recipients
def wiki_content_added(wiki_content, recipient)
def wiki_content_added(wiki_content)
redmine_headers 'Project' => wiki_content.project.identifier,
'Wiki-Page-Id' => wiki_content.page.id,
'Type' => "Wiki"
message_id wiki_content
recipients [recipient]
recipients wiki_content.recipients
cc(wiki_content.page.wiki.watcher_recipients - recipients)
subject "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_added, :id => wiki_content.page.pretty_title)}"
body :wiki_content => wiki_content,
:wiki_content_url => url_for(:controller => 'wiki', :action => 'show', :project_id => wiki_content.project, :id => wiki_content.page.title)
@ -206,12 +213,13 @@ class Mailer < ActionMailer::Base
# Example:
# wiki_content_updated(wiki_content) => tmail object
# Mailer.deliver_wiki_content_updated(wiki_content) => sends an email to the project's recipients
def wiki_content_updated(wiki_content, recipient)
def wiki_content_updated(wiki_content)
redmine_headers 'Project' => wiki_content.project.identifier,
'Wiki-Page-Id' => wiki_content.page.id,
'Type' => "Wiki"
message_id wiki_content
recipients [recipient]
recipients wiki_content.recipients
cc(wiki_content.page.wiki.watcher_recipients + wiki_content.page.watcher_recipients - recipients)
subject "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_updated, :id => wiki_content.page.pretty_title)}"
body :wiki_content => wiki_content,
:wiki_content_url => url_for(:controller => 'wiki', :action => 'show', :project_id => wiki_content.project, :id => wiki_content.page.title),
@ -285,44 +293,6 @@ class Mailer < ActionMailer::Base
render_multipart('register', body)
end
def mail_handler_confirmation(object, user, email_subject)
recipients user.mail
case
when object.is_a?(Issue)
project = object.project.name
url = url_for(:controller => 'issues', :action => 'show', :id => object.id)
when object.is_a?(Journal)
project = object.project.name
url = url_for(:controller => 'issues', :action => 'show', :id => object.issue.id)
when object.class == Message
project = object.project.name
url = url_for(object.event_url)
else
project = ''
url = ''
end
subject "[#{project}] #{l(:label_mail_handler_confirmation, :subject => email_subject)}"
body(:object => object,
:url => url)
render_multipart('mail_handler_confirmation', body)
end
def mail_handler_unauthorized_action(user, email_subject, options={})
recipients options[:to] || user.mail
subject l(:label_mail_handler_failure, :subject => email_subject)
body({})
render_multipart('mail_handler_unauthorized_action', body)
end
def mail_handler_missing_information(user, email_subject, error_message)
recipients user.mail
subject l(:label_mail_handler_failure, :subject => email_subject)
body({:errors => error_message.to_s})
render_multipart('mail_handler_missing_information', body)
end
def test(user)
redmine_headers 'Type' => "Test"
set_language_if_valid(user.language)
@ -425,7 +395,7 @@ class Mailer < ActionMailer::Base
# Removes the current user from the recipients and cc
# if he doesn't want to receive notifications about what he does
@author ||= User.current
if @author && @author.mail && @author.pref[:no_self_notified]
if @author.pref[:no_self_notified]
recipients((recipients.is_a?(Array) ? recipients : [recipients]) - [@author.mail]) if recipients.present?
cc((cc.is_a?(Array) ? cc : [cc]) - [@author.mail]) if cc.present?
end
@ -433,6 +403,13 @@ class Mailer < ActionMailer::Base
notified_users = [recipients, cc].flatten.compact.uniq
# Rails would log recipients only, not cc and bcc
mylogger.info "Sending email notification to: #{notified_users.join(', ')}" if mylogger
# Blind carbon copy recipients
if Setting.bcc_recipients?
bcc(notified_users)
recipients []
cc []
end
super
end

View File

@ -41,6 +41,7 @@ class Message < ActiveRecord::Base
acts_as_watchable
attr_protected :locked, :sticky
validates_presence_of :board, :subject, :content
validates_length_of :subject, :maximum => 255
@ -50,7 +51,7 @@ class Message < ActiveRecord::Base
:conditions => Project.allowed_to_condition(args.first || User.current, :view_messages) } }
safe_attributes 'subject', 'content'
safe_attributes 'locked', 'sticky', 'board_id',
safe_attributes 'locked', 'sticky',
:if => lambda {|message, user|
user.allowed_to?(:edit_messages, message.project)
}
@ -80,15 +81,9 @@ class Message < ActiveRecord::Base
end
def after_destroy
parent.reset_last_reply_id! if parent
board.reset_counters!
end
def reset_last_reply_id!
clid = children.present? ? children.last.id : nil
self.update_attribute(:last_reply_id, clid)
end
def sticky=(arg)
write_attribute :sticky, (arg == true || arg.to_s == '1' ? 1 : 0)
end

View File

@ -14,13 +14,6 @@
class MessageObserver < ActiveRecord::Observer
def after_create(message)
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
Mailer.deliver_message_posted(message) if Setting.notified_events.include?('message_posted')
end
end

View File

@ -22,8 +22,7 @@ class News < ActiveRecord::Base
validates_length_of :title, :maximum => 60
validates_length_of :summary, :maximum => 255
acts_as_journalized :event_url => Proc.new {|o| {:controller => 'news', :action => 'show', :id => o.journaled_id} },
:event_description => :description
acts_as_journalized :event_url => Proc.new {|o| {:controller => 'news', :action => 'show', :id => o.journaled_id} }
acts_as_searchable :columns => ["#{table_name}.title", "#{table_name}.summary", "#{table_name}.description"], :include => :project
acts_as_watchable
@ -40,11 +39,6 @@ class News < ActiveRecord::Base
!user.nil? && user.allowed_to?(:view_news, project)
end
# Returns true if the news can be commented by user
def commentable?(user=User.current)
user.allowed_to?(:comment_news, project)
end
# returns latest news for projects visible by user
def self.latest(user = User.current, count = 5)
find(:all, :limit => count, :conditions => Project.allowed_to_condition(user, :view_news), :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")

View File

@ -14,10 +14,6 @@
class NewsObserver < ActiveRecord::Observer
def after_create(news)
if Setting.notified_events.include?('news_added')
news.recipients.each do |recipient|
Mailer.deliver_news_added(news, recipient)
end
end
Mailer.deliver_news_added(news) if Setting.notified_events.include?('news_added')
end
end

View File

@ -31,10 +31,6 @@ class Principal < ActiveRecord::Base
before_create :set_default_empty_values
def to_liquid
PrincipalDrop.new(self)
end
def name(formatter = nil)
to_s
end
@ -48,82 +44,6 @@ 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)

View File

@ -82,16 +82,6 @@ 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
@ -141,11 +131,6 @@ 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)
@ -631,7 +616,7 @@ class Project < ActiveRecord::Base
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
ancestors.pop
end
yield project, ancestors.size if block_given?
yield project, ancestors.size
ancestors << project
end
end

View File

@ -12,7 +12,64 @@
# 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
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
class Query < ActiveRecord::Base
class StatementInvalid < ::ActiveRecord::StatementInvalid
end
belongs_to :project
belongs_to :user
@ -33,7 +90,6 @@ class Query < ActiveRecord::Base
"*" => :label_all,
">=" => :label_greater_or_equal,
"<=" => :label_less_or_equal,
"><" => :label_between,
"<t+" => :label_in_less_than,
">t+" => :label_in_more_than,
"t+" => :label_in,
@ -51,8 +107,8 @@ class Query < ActiveRecord::Base
:list_status => [ "o", "=", "!", "c", "*" ],
:list_optional => [ "=", "!", "!*", "*" ],
:list_subprojects => [ "*", "!*", "=" ],
:date => [ "=", ">=", "<=", "><", "<t+", ">t+", "t+", "t", "w", ">t-", "<t-", "t-" ],
:date_past => [ "=", ">=", "<=", "><", ">t-", "<t-", "t-", "t", "w" ],
:date => [ "<t+", ">t+", "t+", "t", "w", ">t-", "<t-", "t-" ],
:date_past => [ ">t-", "<t-", "t-", "t", "w" ],
:string => [ "=", "~", "!", "!~" ],
:text => [ "~", "!~" ],
:integer => [ "=", ">=", "<=", "!*", "*" ] }
@ -83,7 +139,6 @@ class Query < ActiveRecord::Base
def initialize(attributes = nil)
super attributes
self.filters ||= { 'status_id' => {:operator => "o", :values => [""]} }
self.display_subprojects ||= Setting.display_subprojects_issues?
end
def after_initialize
@ -190,7 +245,7 @@ class Query < ActiveRecord::Base
def add_filter(field, operator, values)
# values must be an array
return unless values.nil? || values.is_a?(Array)
return unless values and values.is_a? Array # and !values.first.empty?
# check if field is defined as an available filter
if available_filters.has_key? field
filter_options = available_filters[field]
@ -199,7 +254,7 @@ class Query < ActiveRecord::Base
# allowed_values = values & ([""] + (filter_options[:values] || []).collect {|val| val[1]})
# filters[field] = {:operator => operator, :values => allowed_values } if (allowed_values.first and !allowed_values.first.empty?) or ["o", "c", "!*", "*", "t"].include? operator
#end
filters[field] = {:operator => operator, :values => (values || ['']) }
filters[field] = {:operator => operator, :values => values }
end
end
@ -211,9 +266,9 @@ class Query < ActiveRecord::Base
# Add multiple filters using +add_filter+
def add_filters(fields, operators, values)
if fields.is_a?(Array) && operators.is_a?(Hash) && (values.nil? || values.is_a?(Hash))
if fields.is_a?(Array) && operators.is_a?(Hash) && values.is_a?(Hash)
fields.each do |field|
add_filter(field, operators[field], values && values[field])
add_filter(field, operators[field], values[field])
end
end
end
@ -222,10 +277,6 @@ class Query < ActiveRecord::Base
filters and filters[field]
end
def type_for(field)
available_filters[field][:type] if available_filters.has_key?(field)
end
def operator_for(field)
has_filter?(field) ? filters[field][:operator] : nil
end
@ -234,10 +285,6 @@ class Query < ActiveRecord::Base
has_filter?(field) ? filters[field][:values] : nil
end
def value_for(field, index=0)
(values_for(field) || [])[index]
end
def label_for(field)
label = available_filters[field][:name] if available_filters.has_key?(field)
label ||= field.gsub(/\_id$/, "")
@ -363,7 +410,7 @@ class Query < ActiveRecord::Base
# all subprojects
ids += project.descendants.collect(&:id)
end
elsif display_subprojects?
elsif Setting.display_subprojects_issues?
ids += project.descendants.collect(&:id)
end
project_clauses << "#{Project.table_name}.id IN (%s)" % ids.join(',')
@ -383,10 +430,8 @@ class Query < ActiveRecord::Base
next unless v and !v.empty?
operator = operator_for(field)
# "me" value substitution
if %w(assigned_to_id author_id watcher_id).include?(field) ||
# user custom fields
available_filters.has_key?(field) && available_filters[field][:format] == 'user'
# "me" value subsitution
if %w(assigned_to_id author_id watcher_id).include?(field)
v.push(User.current.logged? ? User.current.id.to_s : "0") if v.delete("me")
end
@ -474,7 +519,7 @@ class Query < ActiveRecord::Base
def issue_count
Issue.count(:include => [:status, :project], :conditions => statement)
rescue ::ActiveRecord::StatementInvalid => e
raise Query::StatementInvalid.new(e.message)
raise StatementInvalid.new(e.message)
end
# Returns the issue count by group or nil if query is not grouped
@ -494,7 +539,7 @@ class Query < ActiveRecord::Base
end
r
rescue ::ActiveRecord::StatementInvalid => e
raise Query::StatementInvalid.new(e.message)
raise StatementInvalid.new(e.message)
end
# Returns the issues
@ -509,7 +554,7 @@ class Query < ActiveRecord::Base
:limit => options[:limit],
:offset => options[:offset]
rescue ::ActiveRecord::StatementInvalid => e
raise Query::StatementInvalid.new(e.message)
raise StatementInvalid.new(e.message)
end
# Returns the journals
@ -521,7 +566,7 @@ class Query < ActiveRecord::Base
:limit => options[:limit],
:offset => options[:offset]
rescue ::ActiveRecord::StatementInvalid => e
raise Query::StatementInvalid.new(e.message)
raise StatementInvalid.new(e.message)
end
# Returns the versions
@ -530,7 +575,7 @@ class Query < ActiveRecord::Base
Version.find :all, :include => :project,
:conditions => Query.merge_conditions(project_statement, options[:conditions])
rescue ::ActiveRecord::StatementInvalid => e
raise Query::StatementInvalid.new(e.message)
raise StatementInvalid.new(e.message)
end
private
@ -540,15 +585,11 @@ class Query < ActiveRecord::Base
sql = ''
case operator
when "="
if [:date, :date_past].include?(type_for(field))
sql = date_clause(db_table, db_field, (Date.parse(value.first) rescue nil), (Date.parse(value.first) rescue nil))
if value.present?
sql = "#{db_table}.#{db_field} IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")"
else
if value.any?
sql = "#{db_table}.#{db_field} IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")"
else
# IN an empty set
sql = "0=1"
end
# empty set of allowed values produces no result
sql = "0=1"
end
when "!"
if value.present?
@ -564,58 +605,42 @@ class Query < ActiveRecord::Base
sql = "#{db_table}.#{db_field} IS NOT NULL"
sql << " AND #{db_table}.#{db_field} <> ''" if is_custom_filter
when ">="
if [:date, :date_past].include?(type_for(field))
sql = date_clause(db_table, db_field, (Date.parse(value.first) rescue nil), nil)
if is_custom_filter
sql = "#{db_table}.#{db_field} != '' AND CAST(#{db_table}.#{db_field} AS decimal(60,4)) >= #{value.first.to_f}"
else
if is_custom_filter
sql = "CAST(#{db_table}.#{db_field} AS decimal(60,3)) >= #{value.first.to_i}"
else
sql = "#{db_table}.#{db_field} >= #{value.first.to_i}"
end
sql = "#{db_table}.#{db_field} >= #{value.first.to_f}"
end
when "<="
if [:date, :date_past].include?(type_for(field))
sql = date_clause(db_table, db_field, nil, (Date.parse(value.first) rescue nil))
if is_custom_filter
sql = "#{db_table}.#{db_field} != '' AND CAST(#{db_table}.#{db_field} AS decimal(60,4)) <= #{value.first.to_f}"
else
if is_custom_filter
sql = "CAST(#{db_table}.#{db_field} AS decimal(60,3)) <= #{value.first.to_i}"
else
sql = "#{db_table}.#{db_field} <= #{value.first.to_i}"
end
end
when "><"
if [:date, :date_past].include?(type_for(field))
sql = date_clause(db_table, db_field, (Date.parse(value[0]) rescue nil), (Date.parse(value[1]) rescue nil))
else
if is_custom_filter
sql = "CAST(#{db_table}.#{db_field} AS decimal(60,3)) BETWEEN #{value[0].to_i} AND #{value[1].to_i}"
else
sql = "#{db_table}.#{db_field} BETWEEN #{value[0].to_i} AND #{value[1].to_i}"
end
sql = "#{db_table}.#{db_field} <= #{value.first.to_f}"
end
when "o"
sql = "#{IssueStatus.table_name}.is_closed=#{connection.quoted_false}" if field == "status_id"
when "c"
sql = "#{IssueStatus.table_name}.is_closed=#{connection.quoted_true}" if field == "status_id"
when ">t-"
sql = relative_date_clause(db_table, db_field, - value.first.to_i, 0)
sql = date_range_clause(db_table, db_field, - value.first.to_i, 0)
when "<t-"
sql = relative_date_clause(db_table, db_field, nil, - value.first.to_i)
sql = date_range_clause(db_table, db_field, nil, - value.first.to_i)
when "t-"
sql = relative_date_clause(db_table, db_field, - value.first.to_i, - value.first.to_i)
sql = date_range_clause(db_table, db_field, - value.first.to_i, - value.first.to_i)
when ">t+"
sql = relative_date_clause(db_table, db_field, value.first.to_i, nil)
sql = date_range_clause(db_table, db_field, value.first.to_i, nil)
when "<t+"
sql = relative_date_clause(db_table, db_field, 0, value.first.to_i)
sql = date_range_clause(db_table, db_field, 0, value.first.to_i)
when "t+"
sql = relative_date_clause(db_table, db_field, value.first.to_i, value.first.to_i)
sql = date_range_clause(db_table, db_field, value.first.to_i, value.first.to_i)
when "t"
sql = relative_date_clause(db_table, db_field, 0, 0)
sql = date_range_clause(db_table, db_field, 0, 0)
when "w"
first_day_of_week = l(:general_first_day_of_week).to_i
day_of_week = Date.today.cwday
days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week)
sql = relative_date_clause(db_table, db_field, - days_ago, - days_ago + 6)
from = l(:general_first_day_of_week) == '7' ?
# week starts on sunday
((Date.today.cwday == 7) ? Time.now.at_beginning_of_day : Time.now.at_beginning_of_week - 1.day) :
# week starts on monday (Rails default)
Time.now.at_beginning_of_week
sql = "#{db_table}.#{db_field} BETWEEN '%s' AND '%s'" % [connection.quoted_date(from), connection.quoted_date(from + 7.days)]
when "~"
sql = "LOWER(#{db_table}.#{db_field}) LIKE '%#{connection.quote_string(value.first.to_s.downcase)}%'"
when "!~"
@ -642,32 +667,23 @@ class Query < ActiveRecord::Base
options = { :type => :list, :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]], :order => 20 }
when "user", "version"
next unless project
values = field.possible_values_options(project)
if User.current.logged? && field.field_format == 'user'
values.unshift ["<< #{l(:label_me)} >>", "me"]
end
options = { :type => :list_optional, :values => values, :order => 20}
options = { :type => :list_optional, :values => field.possible_values_options(project), :order => 20}
else
options = { :type => :string, :order => 20 }
end
@available_filters["cf_#{field.id}"] = options.merge({ :name => field.name, :format => field.field_format })
@available_filters["cf_#{field.id}"] = options.merge({ :name => field.name })
end
end
# Returns a SQL clause for a date or datetime field.
def date_clause(table, field, from, to)
def date_range_clause(table, field, from, to)
s = []
if from
s << ("#{table}.#{field} > '%s'" % [connection.quoted_date((from - 1).to_time.end_of_day)])
s << ("#{table}.#{field} > '%s'" % [connection.quoted_date((Date.yesterday + from).to_time.end_of_day)])
end
if to
s << ("#{table}.#{field} <= '%s'" % [connection.quoted_date(to.to_time.end_of_day)])
s << ("#{table}.#{field} <= '%s'" % [connection.quoted_date((Date.today + to).to_time.end_of_day)])
end
s.join(' AND ')
end
# Returns a SQL clause for a date or datetime field using relative dates.
def relative_date_clause(table, field, days_from, days_to)
date_clause(table, field, (days_from ? Date.today + days_from : nil), (days_to ? Date.today + days_to : nil))
end
end

View File

@ -1,16 +0,0 @@
#-- encoding: UTF-8
#-- copyright
# ChiliProject is a project management system.
#
# Copyright (C) 2010-2013 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

View File

@ -1,42 +0,0 @@
#-- encoding: UTF-8
#-- copyright
# ChiliProject is a project management system.
#
# Copyright (C) 2010-2013 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

View File

@ -1,40 +0,0 @@
#-- encoding: UTF-8
#-- copyright
# ChiliProject is a project management system.
#
# Copyright (C) 2010-2013 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

View File

@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require_dependency 'redmine/scm/adapters/bazaar_adapter'
require 'redmine/scm/adapters/bazaar_adapter'
class Repository::Bazaar < Repository
attr_protected :root_url

View File

@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require_dependency 'redmine/scm/adapters/cvs_adapter'
require 'redmine/scm/adapters/cvs_adapter'
require 'digest/sha1'
class Repository::Cvs < Repository

View File

@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require_dependency 'redmine/scm/adapters/darcs_adapter'
require 'redmine/scm/adapters/darcs_adapter'
class Repository::Darcs < Repository
validates_presence_of :url, :log_encoding

View File

@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require_dependency 'redmine/scm/adapters/filesystem_adapter'
require 'redmine/scm/adapters/filesystem_adapter'
class Repository::Filesystem < Repository
attr_protected :root_url

View File

@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require_dependency 'redmine/scm/adapters/git_adapter'
require 'redmine/scm/adapters/git_adapter'
class Repository::Git < Repository
attr_protected :root_url

View File

@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require_dependency 'redmine/scm/adapters/mercurial_adapter'
require 'redmine/scm/adapters/mercurial_adapter'
class Repository::Mercurial < Repository
# sort changesets by revision number

View File

@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require_dependency 'redmine/scm/adapters/subversion_adapter'
require 'redmine/scm/adapters/subversion_adapter'
class Repository::Subversion < Repository
attr_protected :root_url

View File

@ -97,17 +97,13 @@ class Setting < ActiveRecord::Base
# Returns the value of the setting named name
def self.[](name)
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
Marshal.load(Rails.cache.fetch("chiliproject/setting/#{name}") {Marshal.dump(find_or_default(name).value)})
end
def self.[]=(name, v)
setting = find_or_default(name)
setting.value = (v ? v : "")
Rails.cache.delete self.cache_key(name)
Rails.cache.delete "chiliproject/setting/#{name}"
setting.save
setting.value
end
@ -141,42 +137,23 @@ class Setting < ActiveRecord::Base
Object.const_defined?(:OpenID) && self[:openid].to_i > 0
end
# Deprecation Warning: This method is no longer available. There is no
# replacement.
# Checks if settings have changed since the values were read
# and clears the cache hash if it's the case
# Called once per request
def self.check_cache
# 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."
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
end
# Clears all of the Setting caches
def self.clear_cache
# 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
Rails.cache.delete_matched( /^chiliproject\/setting\/.+$/ )
Rails.cache.write('chiliproject/setting-cleared_on', Marshal.dump(Time.now))
logger.info 'Settings cache cleared.' if logger
end
private
@ -185,13 +162,7 @@ private
def self.find_or_default(name)
name = name.to_s
raise "There's no setting named #{name}" unless @@available_settings.has_key?(name)
find_by_name(name) or new do |s|
s.name = name
s.value = @@available_settings[name]['default']
end
end
def self.cache_key(name)
"chiliproject/setting/#{Setting.maximum(:updated_on).to_i}/#{name}"
setting = find_by_name(name)
setting ||= new(:name => name, :value => @@available_settings[name]['default']) if @@available_settings.has_key? name
end
end

View File

@ -35,10 +35,6 @@ class Tracker < ActiveRecord::Base
name <=> tracker.name
end
def to_liquid
TrackerDrop.new(self)
end
def self.all
find(:all, :order => 'position')
end

View File

@ -64,9 +64,10 @@ class User < Principal
validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
# Login must contain lettres, numbers, underscores only
validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
validates_length_of :login, :firstname, :lastname, :maximum => 255
validates_length_of :login, :maximum => 30
validates_length_of :firstname, :lastname, :maximum => 30
validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
validates_length_of :mail, :maximum => 255, :allow_nil => true
validates_length_of :mail, :maximum => 60, :allow_nil => true
validates_confirmation_of :password, :allow_nil => true
validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
validates_inclusion_of :status, :in => [STATUS_ANONYMOUS, STATUS_ACTIVE, STATUS_REGISTERED, STATUS_LOCKED]
@ -344,6 +345,27 @@ 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?
@ -366,6 +388,53 @@ 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',
@ -403,8 +472,6 @@ class User < Principal
when 'only_my_events'
if object.is_a?(Issue) && (object.author == self || object.assigned_to == self)
true
elsif object.respond_to?(:watched_by?) && object.watched_by?(self) # Make it clear that we always want to be notified about things we watch in this case
true
else
false
end

View File

@ -27,7 +27,7 @@ class Version < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :name, :scope => [:project_id]
validates_length_of :name, :maximum => 60
validates_format_of :start_date, :effective_date, :with => /^\d{4}-\d{2}-\d{2}$/, :message => :not_a_date, :allow_nil => true
validates_format_of :effective_date, :with => /^\d{4}-\d{2}-\d{2}$/, :message => :not_a_date, :allow_nil => true
validates_inclusion_of :status, :in => VERSION_STATUSES
validates_inclusion_of :sharing, :in => VERSION_SHARINGS
@ -37,7 +37,6 @@ class Version < ActiveRecord::Base
safe_attributes 'name',
'description',
'start_date',
'effective_date',
'due_date',
'wiki_page_title',

View File

@ -14,7 +14,7 @@
class Watcher < ActiveRecord::Base
belongs_to :watchable, :polymorphic => true
belongs_to :user, :class_name => 'Principal', :foreign_key => 'user_id'
belongs_to :user
validates_presence_of :user
validates_uniqueness_of :user_id, :scope => [:watchable_type, :watchable_id]

View File

@ -98,7 +98,7 @@ class WikiContent < ActiveRecord::Base
changes.delete("text")
changes["data"] = hash[:text]
changes["compression"] = hash[:compression]
update_attribute(:changes, changes)
update_attribute(:changes, changes.to_yaml)
end
def text

View File

@ -53,10 +53,6 @@ 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

View File

@ -86,8 +86,8 @@ class Workflow < ActiveRecord::Base
else
transaction do
delete_all :tracker_id => target_tracker.id, :role_id => target_role.id
connection.insert "INSERT INTO #{Workflow.table_name} (tracker_id, role_id, old_status_id, new_status_id, author, assignee)" +
" SELECT #{target_tracker.id}, #{target_role.id}, old_status_id, new_status_id, author, assignee" +
connection.insert "INSERT INTO #{Workflow.table_name} (tracker_id, role_id, old_status_id, new_status_id)" +
" SELECT #{target_tracker.id}, #{target_role.id}, old_status_id, new_status_id" +
" FROM #{Workflow.table_name}" +
" WHERE tracker_id = #{source_tracker.id} AND role_id = #{source_role.id}"
end

View File

@ -1,35 +0,0 @@
<div id="nav-login-content">
<% form_tag({:controller => "account", :action=> "login"}) do %>
<%= hidden_field_tag 'back_url', CGI.escape(request.url), :id => nil %>
<table>
<tr>
<td><label for="username-pulldown"><%= l(:field_login) %></label></td>
<td><label for="password-pulldown"><%= l(:field_password) %></label></td>
<td></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>

View File

@ -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 => '2' %></td>
<td align="left"><%= text_field_tag 'username', nil, :tabindex => '1' %></td>
</tr>
<tr>
<td align="right"><label for="password"><%=l(:field_password)%>:</label></td>

View File

@ -1,6 +1,6 @@
<h2><%=l(:label_register)%> <%=link_to l(:label_login_with_open_id_option), signin_url if Setting.openid? %></h2>
<% form_tag({:action => 'register'}, :class => "tabular", :autocomplete => :off) do %>
<% form_tag({:action => 'register'}, :class => "tabular") do %>
<%= error_messages_for 'user' %>
<div class="box">

View File

@ -42,7 +42,6 @@
<% content_for :sidebar do %>
<% form_tag({}, :method => :get) do %>
<h3><%= l(:label_activity) %></h3>
<%= hidden_field_tag "set_filter", 1, :id => nil %>
<p><% @activity.event_types.each do |t| %>
<%= check_box_tag "show_#{t}", 1, @activity.scope.include?(t) %>
<label for="show_<%=t%>"><%= link_to(l("label_#{t.singularize}_plural"), {"show_#{t}" => 1, :user_id => params[:user_id]})%></label>

View File

@ -1 +1,5 @@
<%= render_menu :admin_menu %>
<div id="admin-menu">
<ul>
<%= render_menu :admin_menu %>
</ul>
</div>

View File

@ -1,3 +1,5 @@
<h2><%=l(:label_administration)%></h2>
<div id="admin-index">
<%= render :partial => 'no_data' if @no_configuration_data %>
<%= render :partial => 'menu' %>

View File

@ -1 +0,0 @@
<%= projects_check_box_tags 'project_ids[]', @projects %>

View File

@ -71,13 +71,3 @@
<%= 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 %>

View File

@ -1,55 +1,52 @@
<ul class="menu">
<ul>
<%= call_hook(:view_issues_context_menu_start, {:issues => @issues, :can => @can, :back => @back }) %>
<% if !@issue.nil? -%>
<li class="edit"><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue},
<li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue},
:class => 'icon-edit', :disabled => !@can[:edit] %></li>
<% else %>
<li class="edit"><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id)},
<li><%= 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 status">
<a href="#" class="context_item" onclick="return false;"><%= l(:field_status) %></a>
<li class="folder">
<a href="#" class="submenu" 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 tracker">
<a href="#" class="context_item"><%= l(:field_tracker) %></a>
<li class="folder">
<a href="#" class="submenu"><%= 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 priority">
<a href="#" class="context_item"><%= l(:field_priority) %></a>
<li class="folder">
<a href="#" class="submenu"><%= 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 fixed_version">
<a href="#" class="context_item"><%= l(:field_fixed_version) %></a>
<li class="folder">
<a href="#" class="submenu"><%= 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,
@ -58,13 +55,11 @@
<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 assigned">
<a href="#" class="context_item"><%= l(:field_assigned_to) %></a>
<li class="folder">
<a href="#" class="submenu"><%= 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,
@ -73,13 +68,11 @@
<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="context_item"><%= l(:field_category) %></a>
<a href="#" class="submenu"><%= 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,
@ -88,30 +81,28 @@
<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>
</li>
<% end -%>
<% if Issue.use_field_for_done_ratio? %>
<li class="folder done_ratio">
<a href="#" class="context_item"><%= l(:field_done_ratio) %></a>
<li class="folder">
<a href="#" class="submenu"><%= 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 class="log_time"><%= context_menu_link l(:button_log_time), {:controller => 'timelog', :action => 'new', :issue_id => @issue},
:class => 'context_item' %></li>
<li><%= context_menu_link l(:button_log_time), {:controller => 'timelog', :action => 'new', :issue_id => @issue},
:class => 'icon-time-add' %></li>
<% end %>
<% if User.current.logged? %>
<li class="watch"><%= watcher_link(@issue, User.current) %></li>
<li><%= watcher_link(@issue, User.current) %></li>
<% end %>
<% end %>
@ -119,13 +110,12 @@
<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 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>
<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>
<%= call_hook(:view_issues_context_menu_end, {:issues => @issues, :can => @can, :back => @back }) %>
</ul>

View File

@ -9,15 +9,6 @@
<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>

View File

@ -27,16 +27,6 @@
<% 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 %>

View File

@ -72,7 +72,7 @@ t_height = g_height + headers_height
<p class="warning"><%= l(:notice_gantt_chart_truncated, :max => @gantt.max_rows) %></p>
<% end %>
<table style="width:100%; border:0; border-collapse: collapse;">
<table width="100%" style="border:0; border-collapse: collapse;">
<tr>
<td style="width:<%= subject_width %>px; padding:0px;">
@ -98,7 +98,7 @@ month_f = @gantt.date_from
left = 0
height = (show_weeks ? header_heigth : header_heigth + g_height)
@gantt.months.times do
width = (((month_f >> 1) - month_f) * zoom - 1).to_i
width = ((month_f >> 1) - month_f) * zoom - 1
%>
<div style="left:<%= left %>px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">
<%= link_to h("#{month_f.year}-#{month_f.month}"), @gantt.params.merge(:year => month_f.year, :month => month_f.month), :title => "#{month_name(month_f.month)} #{month_f.year}"%>
@ -176,7 +176,7 @@ if Date.today >= @gantt.date_from and Date.today <= @gantt.date_to %>
</tr>
</table>
<table style="width:100%">
<table width="100%">
<tr>
<td align="left"><%= link_to_content_update('&#171; ' + l(:label_previous), params.merge(@gantt.params_previous)) %></td>
<td align="right"><%= link_to_content_update(l(:label_next) + ' &#187;', params.merge(@gantt.params_next)) %></td>

View File

@ -41,5 +41,17 @@
</div>
<div class="splitcontentright">
<%= render :partial => 'members/membership_assignment', :locals => {:principal => @group, :projects => projects - @group.projects, :roles => roles } %>
<% 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 %>
</div>

View File

@ -33,7 +33,7 @@
<%= observe_field(:user_search,
:frequency => 0.5,
:update => :users,
:url => auto_complete_users_path(:remove_group_members => @group),
:url => { :controller => 'groups', :action => 'autocomplete_for_user', :id => @group },
:with => 'q')
%>

View File

@ -19,7 +19,6 @@
<td class="buttons"><%= link_to l(:button_delete), group, :confirm => l(:text_are_you_sure), :method => :delete, :class => 'icon icon-del' %></td>
</tr>
<% end %>
</tbody>
</table>
<% else %>
<p class="nodata"><%= l(:label_no_data) %></p>

View File

@ -12,7 +12,7 @@
<% html_title "Wiki Syntax Quick Reference" %>
<h1>Wiki Syntax Quick Reference</h1>
<table style="width:100%">
<table width="100%">
<tr><th colspan="3">Font Styles</th></tr>
<tr><th><img src="../images/jstoolbar/bt_strong.png" style="border: 1px solid #bbb;" alt="Strong" /></th><td width="50%">*Strong*</td><td width="50%"><strong>Strong</strong></td></tr>
<tr><th><img src="../images/jstoolbar/bt_em.png" style="border: 1px solid #bbb;" alt="Italic" /></th><td>_Italic_</td><td><em>Italic</em></td></tr>

View File

@ -13,22 +13,20 @@
}
a.new { color: #b73535; }
.syntaxhl .line-numbers { padding: 2px 4px 2px 4px; background-color: #eee; margin:0 }
.syntaxhl .comment { color:#666; }
.CodeRay .c { color:#666; }
.syntaxhl .class { color:#B06; font-weight:bold }
.syntaxhl .delimiter { color:black }
.syntaxhl .function { color:#06B; font-weight:bold }
.CodeRay .cl { color:#B06; font-weight:bold }
.CodeRay .dl { color:black }
.CodeRay .fu { color:#06B; font-weight:bold }
.syntaxhl .inline { background: #eee }
.syntaxhl .inline .inline-delimiter { font-weight: bold; color: #888 }
.CodeRay .il { background: #eee }
.CodeRay .il .idl { font-weight: bold; color: #888 }
.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 .iv { color:#33B }
.CodeRay .r { color:#080; font-weight:bold }
.CodeRay .s { background-color:#fff0f0 }
.CodeRay .s .dl { color:#710 }
<% end %>
<% html_title "Wiki Formatting" %>
@ -238,7 +236,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>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>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>You can highlight code in your wiki page using this syntax:</p>
@ -250,14 +248,15 @@ To go live, all you need to add is a database and a web server.
<p>Example:</p>
<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><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>

View File

@ -46,12 +46,12 @@
<div class="splitcontentright">
<p>
<label for='start_date'><%= l(:field_start_date) %></label>
<%= date_field_tag 'start_date', '', :size => 10 %>
<%= text_field_tag 'start_date', '', :size => 10 %><%= calendar_for('start_date') %>
</p>
<p>
<label for='due_date'><%= l(:field_due_date) %></label>
<%= date_field_tag 'due_date', '', :size => 10 %>
<%= text_field_tag 'due_date', '', :size => 10 %><%= calendar_for('due_date') %>
</p>
</div>

View File

@ -31,8 +31,8 @@
</div>
<div class="splitcontentright">
<p><%= f.date_field :start_date, :size => 10, :disabled => !@issue.leaf? %><%= calendar_for('issue_start_date') if @issue.leaf? %></p>
<p><%= f.date_field :due_date, :size => 10, :disabled => !@issue.leaf? %><%= calendar_for('issue_due_date') if @issue.leaf? %></p>
<p><%= f.text_field :start_date, :size => 10, :disabled => !@issue.leaf? %><%= calendar_for('issue_start_date') if @issue.leaf? %></p>
<p><%= f.text_field :due_date, :size => 10, :disabled => !@issue.leaf? %><%= calendar_for('issue_due_date') if @issue.leaf? %></p>
<p><%= f.text_field :estimated_hours, :size => 3, :disabled => !@issue.leaf? %> <%= l(:field_hours) %></p>
<% if @issue.leaf? && Issue.use_field_for_done_ratio? %>
<p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p>

View File

@ -38,7 +38,7 @@
<%= call_hook(:view_issues_edit_notes_bottom, { :issue => @issue, :notes => @notes, :form => f }) %>
</fieldset>
<fieldset id="attachments" class="header_collapsible collapsible collapsed"><legend onclick="toggleFieldset(this);"><%=l(:label_attachment_plural)%></legend>
<fieldset id="attachments" class="collapsible collapsed borders"><legend onclick="toggleFieldset(this);"><%=l(:label_attachment_plural)%></legend>
<div style="display: none;">
<%= render :partial => 'attachments/form' %>
</div>

View File

@ -1,9 +1,10 @@
<% form_tag({}) do -%>
<%= hidden_field_tag 'back_url', url_for(params), :id => nil %>
<%= hidden_field_tag 'back_url', url_for(params) %>
<div class="autoscroll">
<table class="list issues">
<thead><tr>
<th class="checkbox hide-when-print"><%= link_to image_tag('toggle_check.png'), {}, :title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}" %>
<th class="checkbox hide-when-print"><%= link_to image_tag('toggle_check.png'), {}, :onclick => 'toggleIssuesSelection(Element.up(this, "form")); return false;',
:title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}" %>
</th>
<%= sort_header_tag('id', :caption => '#', :default_order => 'desc') %>
<% query.columns.each do |column| %>

View File

@ -1,9 +1,10 @@
<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>
<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>
<% if @relations.present? %>
<table style="width:100%">

View File

@ -1,7 +1,17 @@
<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 unless @project %>
<%= render_sidebar_queries %>
<%= call_hook(:view_issues_sidebar_queries_bottom) %>

View File

@ -1,11 +0,0 @@
<% if !@issue.leaf? || @issue.parent || User.current.allowed_to?(:manage_subtasks, @project) %>
<hr />
<p>
<strong><%= l(:label_issue_hierarchy) %></strong>
<% if User.current.allowed_to?(:manage_subtasks, @project) %>
(<%= link_to(l(:label_subtask_add), {:controller => 'issues', :action => 'new', :project_id => @project, :issue => {:parent_issue_id => @issue}}) %>)
<% end %>
</p>
<% end %>
<%= render_parents_and_subtree @issue %>

View File

@ -65,11 +65,11 @@
<% end %>
<p>
<label for='issue_start_date'><%= l(:field_start_date) %></label>
<%= date_field_tag 'issue[start_date]', '', :size => 10 %>
<%= text_field_tag 'issue[start_date]', '', :size => 10 %><%= calendar_for('issue_start_date') %>
</p>
<p>
<label for='issue_due_date'><%= l(:field_due_date) %></label>
<%= date_field_tag 'issue[due_date]', '', :size => 10 %>
<%= text_field_tag 'issue[due_date]', '', :size => 10 %><%= calendar_for('issue_due_date') %>
</p>
<% if Issue.use_field_for_done_ratio? %>
<p>

View File

@ -1,29 +1,23 @@
<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="header_collapsible collapsible <%= @query.new_record? ? "" : "collapsed" %>">
<fieldset id="filters" class="collapsible <%= @query.new_record? ? "" : "collapsed" %>">
<legend onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend>
<div class="filter-fields" style="<%= @query.new_record? ? "" : "display: none;" %>">
<div style="<%= @query.new_record? ? "" : "display: none;" %>">
<%= render :partial => 'queries/filters', :locals => {:query => @query} %>
</div>
</fieldset>
<fieldset id="column_options" class="header_collapsible collapsible collapsed">
<fieldset class="collapsible collapsed">
<legend onclick="toggleFieldset(this);"><%= l(:label_options) %></legend>
<div style="display: none;">
<table>
@ -35,20 +29,6 @@
<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>
@ -75,9 +55,6 @@
</p>
<% end %>
</div><!-- .title-bar-extras -->
</div><!-- .title-bar -->
<%= error_messages_for 'query' %>
<% if @query.valid? %>
<% if @issues.empty? %>

View File

@ -1,84 +1,77 @@
<div class="title-bar" id="upper-title-bar">
<div class="title-bar-actions">
<%= render :partial => 'action_menu' %>
</div>
</div>
<%= render :partial => 'action_menu' %>
<h2><%= h(@issue.tracker.name) %> #<%= h(@issue.id) %><%= call_hook(:view_issues_show_identifier, :issue => @issue) %></h2>
<div class="<%= @issue.css_classes %> details">
<%= avatar(@issue.author, :size => "50") %>
<h1 class="subject">
<%=h(@issue.subject) %> (<%= h(@issue.tracker.name) + ' #' +@issue.id.to_s %>)
</h1>
<hr />
<div class="subject">
<%= render_issue_subject_with_tree(@issue) %>
</div>
<p class="author">
<%= authoring @issue.created_on, @issue.author %>.
<% if @issue.created_on != @issue.updated_on %>
<%= l(:label_updated_time, time_tag(@issue.updated_on)) %>.
<% end %>
</p>
<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)) %>.
<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>
<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>
<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>
<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 %>
</p>
<hr />
<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>
<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>
<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>
<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>
<% else %>
<th></th><td></td>
<% end %>
</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>
<% else %>
<th></th><td></td>
<% end %>
</tr>
<%= render_custom_fields_rows(@issue) %>
<%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %>
</table>
</div><!-- .meta -->
</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>
<% if @issue.description? || @issue.attachments.any? -%>
<hr />
<% if @issue.description? %>
<hr />
<div class="contextual">
<%= link_to_remote_if_authorized(l(:button_quote), { :url => {:controller => 'journals', :action => 'new', :id => @issue} }, :class => 'icon icon-comment') %>
</div>
<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>
<p><strong><%=l(:field_description)%></strong></p>
<div class="wiki">
<%= textilizable @issue, :description, :attachments => @issue.attachments %>
</div>
</div>
<p><strong><%=l(:field_description)%></strong></p>
<div class="wiki">
<%= textilizable @issue, :description, :attachments => @issue.attachments %>
</div>
<% end %>
<% if @issue.attachments.any? -%>
<hr />
<%= link_to_attachments @issue %>
<%= link_to_attachments @issue %>
<% end -%>
<%= call_hook(:view_issues_show_description_bottom, :issue => @issue) %>
<%= render :partial => 'tree_simple' %>
<% 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>
<%= render_descendants_tree(@issue) unless @issue.leaf? %>
</div>
<% end %>
<% if authorize_for('issue_relations', 'new') || @issue.relations.present? %>
<hr />
@ -87,7 +80,6 @@
</div>
<% end %>
<hr />
</div>
<% if @changesets.present? %>
@ -99,19 +91,14 @@
<% if @journals.present? %>
<div id="history">
<h3 class="rounded-background"><%=l(:label_history)%></h3>
<h3><%=l(:label_history)%></h3>
<%= render :partial => 'history', :locals => { :issue => @issue, :journals => @journals } %>
</div>
<% end %>
<div style="clear: both;"></div>
<div class="title-bar" id="lower-title-bar">
<div class="title-bar-actions">
<%= render :partial => 'action_menu', :locals => {:replace_watcher => 'watcher2' } %>
</div>
</div>
<%= render :partial => 'action_menu' %>
<div style="clear: both;"></div>
<% if authorize_for('issues', 'edit') %>
@ -142,8 +129,9 @@
<% content_for :header_tags do %>
<%= auto_discovery_link_tag(:atom, {:format => 'atom', :key => User.current.rss_key}, :title => "#{@issue.project} - #{@issue.tracker} ##{@issue.id}: #{@issue.subject}") %>
<%= stylesheet_link_tag 'scm' %>
<%= javascript_include_tag 'context_menu.jquery' %>
<%= javascript_include_tag 'context_menu' %>
<%= stylesheet_link_tag 'context_menu' %>
<%= stylesheet_link_tag 'context_menu_rtl' if l(:direction) == 'rtl' %>
<% end %>
<%= javascript_tag "jQuery(document).ContextMenu('#{issues_context_menu_path}')" %>
<div id="context-menu" style="display: none;"></div>
<%= javascript_tag "new ContextMenu('#{issues_context_menu_path}')" %>

View File

@ -1,9 +0,0 @@
<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}" %>

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